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.