It gets even better: I'm getting the following exception:
 
com.orientechnologies.orient.core.exception.OStorageException: Cannot open 
local storage 'ow' with mode=rw
 at com.orientechnologies.orient.core.storage.impl.local.
OAbstractPaginatedStorage.open(OAbstractPaginatedStorage.java:220) ~[na:na]
 at com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx.open(
ODatabaseDocumentTx.java:244) ~[na:na]
 at com.orientechnologies.orient.core.db.
OPartitionedDatabasePool$DatabaseDocumentTxPolled.internalOpen(
OPartitionedDatabasePool.java:137) ~[na:na]
 at com.orientechnologies.orient.core.db.OPartitionedDatabasePool.
openDatabase(OPartitionedDatabasePool.java:331) ~[na:na]
 at com.orientechnologies.orient.core.db.OPartitionedDatabasePool.acquire(
OPartitionedDatabasePool.java:304) ~[na:na]
 at com.tinkerpop.blueprints.impls.orient.OrientBaseGraph.<init>(
OrientBaseGraph.java:166) ~[na:na]
 at com.tinkerpop.blueprints.impls.orient.OrientTransactionalGraph.<init>(
OrientTransactionalGraph.java:78) ~[na:na]
 at com.tinkerpop.blueprints.impls.orient.OrientGraph.<init>(OrientGraph.
java:128) ~[na:na]
 at com.tinkerpop.blueprints.impls.orient.OrientGraphFactory.getTx(
OrientGraphFactory.java:74) ~[na:na]
 ...
 ... 7 common frames omitted
Caused by: com.orientechnologies.orient.core.exception.
OSerializationException: Cannot load database's configuration. The database 
seems to be corrupted.
 at 
com.orientechnologies.orient.core.storage.impl.local.OStorageConfigurationSegment.load(OStorageConfigurationSegment.java:84)
 
~[na:na]
 at 
com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage.open(OAbstractPaginatedStorage.java:166)
 
~[na:na]
 ... 34 common frames omitted
Caused by: java.io.IOException: The process cannot access the file because 
another process has locked a portion of the file
 at java.io.RandomAccessFile.read0(Native Method) ~[na:1.7.0_71]
 at java.io.RandomAccessFile.read(RandomAccessFile.java:330) ~[na:1.7.0_71]
 at 
com.orientechnologies.orient.core.storage.fs.OAbstractFile.openChannel(OAbstractFile.java:641)
 
~[na:na]
 at 
com.orientechnologies.orient.core.storage.fs.OAbstractFile.open(OAbstractFile.java:144)
 
~[na:na]
 at 
com.orientechnologies.orient.core.storage.impl.local.OSingleFileSegment.open(OSingleFileSegment.java:57)
 
~[na:na]
 at 
com.orientechnologies.orient.core.storage.impl.local.OStorageConfigurationSegment.load(OStorageConfigurationSegment.java:64)
 
~[na:na]
 ... 35 common frames omitted

Here's what happens:

   - ODatabaseDocumentTx has a field has a field storage. In my case storage is 
   a OLocalPaginatedStorage (I'm using a plocal database).
   - OLocalPaginatedStorage has a field configuration (inherited from 
   OStorageAbstract). In my case configuration is a 
   OStorageConfigurationSegment.
   - OStorageConfigurationSegment has a field segment, of type 
   OSingleFileSegment.

The problem occurs when OAbstractPaginatedStorage.open() does:

configuration.load():

This causes the configuration to attempt:

segment.open();

which will throw this exception...

java.io.IOException: The process cannot access the file because another 
process has locked a portion of the file

...if the file is already open (according to the error message, if it's 
locked by another process; I cannot verify this because the method that 
ultimately fails, java.io.RandomAccessFile.read0(), is native).
It took quite a lot of debugging to find out who may be causing this and 
even now I cannot be 100% sure, but here's what I found:

The database I got from the factory like this...

ODatabaseDocumentTx database = factory.getDatabase();

...does *not* close the configuration when I do database.close(). (Or 
rather, it tries but nothing happens).
Here's ODatabaseDocumentTx.close():

  @Override
  public void close() {
    localCache.shutdown();

    if (isClosed())
      return;

    setCurrentDatabaseInThreadLocal();
    try {
      commit(true);
    } catch (Exception e) {
      OLogManager.instance().error(this, "Exception during commit of active 
transaction.", e);
    }

    if (status != STATUS.OPEN)
      return;

    callOnCloseListeners();

    if (currentIntent != null) {
      currentIntent.end(this);
      currentIntent = null;
    }

    status = STATUS.CLOSED;

    localCache.clear();

    if (!keepStorageOpen && storage != null)
      storage.close(); // <---------------------- this gets invoked

    ODatabaseRecordThreadLocal.INSTANCE.remove();
  }

OStorageAbstract.close(): 
  public void close() {
    close(false, false);
  }

OAbstractPaginatedStorage.close(boolean, boolean):
  public void close(final boolean force, boolean onDelete) {
    doClose(force, onDelete);
  }

OAbstractPaginatedStorage.doClose(boolean, boolean):
  private void doClose(boolean force, boolean onDelete) {
    if (!force && !onDelete)  // <------- because OStorageAbstract.close() 
invoked this
                              // with (false, false), we return in the next 
line => the
                              // storage isn't closed
      return;

    if (status == STATUS.CLOSED)
      return;

    final long timer = Orient.instance().getProfiler().startChrono();

    lock.acquireExclusiveLock();
    try {
      if (status == STATUS.CLOSED)
        return;

      status = STATUS.CLOSING;

      if (!onDelete)
        makeFullCheckpoint();

      preCloseSteps();

      for (OCluster cluster : clusters)
        if (cluster != null)
          cluster.close(!onDelete);

      clusters.clear();
      clusterMap.clear();

      if (configuration != null)
        configuration.close(); // <-------- we don't get here

      super.close(force, onDelete);

      diskCache.removeLowDiskSpaceListener(this);
      if (writeAheadLog != null)
        writeAheadLog.removeFullCheckpointListener(this);

      if (!onDelete)
        diskCache.close();
      else
        diskCache.delete();

      if (writeAheadLog != null) {
        writeAheadLog.close();
        if (onDelete)
          writeAheadLog.delete();
      }

      postCloseSteps(onDelete);

      status = STATUS.CLOSED;
    } catch (IOException e) {
      OLogManager.instance().error(this, "Error on closing of storage '" + 
name, e, OStorageException.class);
    } finally {
      lock.releaseExclusiveLock();

      Orient.instance().getProfiler().stopChrono("db." + name + ".close", 
"Close 
a database", timer, "db.*.close");
    }
  }

As indicated in the comments, the storage remains open even though I've 
closed the database.
Now when the other database instance (the one that is in the factory's 
pool) tries to open the configuration it gets:
IOException: The process cannot access the file because another process has 
locked a portion of the file

Help?
Thanks,
Alicia

On Thursday, July 23, 2015 at 4:56:26 PM UTC+2, Alicia Lobo wrote:
>
> Hi,
> I'm having trouble related to ODatabaseRecordThreadLocal in a 
> *single-threaded* application, in particular: ODatabaseException: 
> Database instance is not set in current thread.
> I understand why it's happening (I'll explain in a moment) and I'm trying 
> to find a way around it that it doesn't pollute my code.
> Here's a really basic scenario:
>
> private OrientGraphFactory factory;
>
> public void someMethod() {
>     ODatabaseDocumentTx database = factory.getDatabase();
>     try {
>         // work with the database
>     } finally {
>         database.close();
>     }
> }
>
> This works fine. Problems start when I have something like this:
>
> public void someMethod() {
> (1) ODatabaseDocumentTx database = factory.getDatabase();
>     try {
>         // work with the database
> (2)     someOtherMethod(); // <-- nested open/close
> (5)     // work with the database
>     } finally {
> (6)     database.close();
>     }
> }
>
> public void someOtherMethod() {
> (3) ODatabaseDocumentTx database = factory.getDatabase();
>     try {
>         // work with the database
>     } finally {
> (4)     database.close();
>     }
> }
>
> If I got it right, this is what happens:
>
>    1. someMethod gets a database from the factory. This database is 
>    stored in ODatabaseRecordThreadLocal.INSTANCE.
>    2. someMethod invokes someOtherMethod.
>    3. someOtherMethod gets a database from the factory. This database is 
>    stored in ODatabaseRecordThreadLocal.INSTANCE, overwriting the one 
>    that someMethod got before.
>    4. someOtherMethod closes the database. 
>    ODatabaseRecordThreadLocal.INSTANCE is cleared (set to null) as a 
>    result.
>    5. someMethod tries to continue working with the database but the 
>    ODatabaseRecordThreadLocal.INSTANCE is no longer valid and I get: 
> ODatabaseException: 
>    Database instance is not set in current thread.
>    
> I looked into it and read this 
> <http://orientdb.com/docs/last/Java-Multi-Threading.html>:
>
>> Since v2.1 OrientDB doesn't allow implicit usage of multiple database 
>> instances from the same thread. Any attempt to manage multiple instances in 
>> the same thread must explicitly call the method 
>> db.activateOnCurrentThread() against the database instance BEFORE you use it
>> .
>>
>
> I couldn't find method activateOnCurrentThread() (was it removed?) but I 
> guess I can just do:
>
> ODatabaseRecordThreadLocal.INSTANCE.set(database);
>
> However, since I saw this warning about using multiple database instances 
> in the same thread, I thought that rather than doing this I would simply 
> reuse the same ODatabaseDocumentTx by storing it locally rather than 
> getting one from the factory every time. Since I'm working in a 
> single-threaded scenario there's no problem sharing this one instance 
> between methods. I added a bit of logic to make sure that the database 
> doesn't get closed by someOtherMethod if someMethod still wants to use it 
> -- this is hidden from the methods in a DBHandler (written by myself) 
> that keeps track of this stuff:
>
> public void someMethod() {
>     ODatabaseDocumentTx database = dbHandler.getDatabase();
>     try {
>         // work with the database
>         someOtherMethod(); // <-- nested open/close
>         // work with the database
>     } finally {
>         dbHandler.closeDatabase();
>     }
> }
>
> public void someOtherMethod() {
>     ODatabaseDocumentTx database = dbHandler.getDatabase();
>     try {
>         // work with the database
>     } finally {
>         dbHandler.closeDatabase();
>     }
> }
>
> Everything works fine. There's only one instance of ODatabaseDocumentTx in 
> the thread.
> However, trouble starts when I want to get a graph. I invoke this method 
> in OrientGraphFactory:
>
>   /**
>    * Gets transactional graph with the database from pool if pool is 
> configured. Otherwise creates a graph with new db instance. The
>    * Graph instance inherits the factory's configuration.
>    *
>    * @return transactional graph
>    */
>   public OrientGraph getTx() {
>     final OrientGraph g;
>     if (pool == null) {
> (1)   g = new OrientGraph(getDatabase(), user, password, settings);
>     } else {
>       // USE THE POOL
> (2)   g = new OrientGraph(pool, settings);
>     }
>     initGraph(g);
>     return g;
>   }
>
> ...which will either instantiate a new database (1) or get one from the 
> pool (2). Since I want to always use the same database in the same thread, 
> I set up the pool in the factory:
>
> factory.setupPool(1, 10);
>
> ...so I guess graphs are ok like that (they don't trigger more database 
> instantiation). However, I cannot find a way to put my database, initially 
> obtained like this...
>
> ODatabaseDocumentTx database = factory.getDatabase();
>
> ...into the pool so that it gets picked up by the factory when I invoke:
>
> factory.getTx();
>
> So I will have 2 database instances: the one I got with 
> factory.getDatabase() and the the one that is in the pool and will be 
> used in factory.getTx().
> What am I supposed to do? IMO this solution...
>
> ODatabaseRecordThreadLocal.INSTANCE.set(database);
>
> ...isn't good because there are still several database instances floating 
> around in the same thread, which (if I understand the documentation 
> correctly) is not a good thing. Plus I don't want to pollute my code with 
> ODatabaseRecordThreadLocal.INSTANCE.set(database) just to deal with the 
> possibility that someone somewhere has changed the thread-local database. I 
> want to use 1 single instance but it seems that OrientDB won't let me, 
> since it will instantiate databases in code over which I have no control.
> What can I do?
>
> I hope this made sense. I'm kinda tired...
> Thanks,
> Alicia
>

-- 

--- 
You received this message because you are subscribed to the Google Groups 
"OrientDB" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to