You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@jackrabbit.apache.org by Nicolas Belisle <Ni...@bibl.ulaval.ca> on 2005/07/06 20:42:16 UTC
Problems with concurrent sessions
Hi,
I'm a Jackrabbit newbie. I'm doing some tests with the tool and I'm having
problem with concurrent sessions.
I have joined my (simple) test class (JCRTest) and the exception I get from
running it.
Anyone had similar problems ?
Regards,
Nicolas
-->The exception report:
java.util.NoSuchElementException: cdeab285-fdbc-4af3-918a-bf4316a29276
at org.apache.jackrabbit.core.LazyItemIterator.next(LazyItemIterator.java:157)
at
org.apache.jackrabbit.core.LazyItemIterator.nextNode(LazyItemIterator.java:98)
at app.JCRTest$ThreadDeleter.run(JCRTest.java:107)
-->The class:
package app;
import java.util.Hashtable;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.observation.Event;
import javax.jcr.observation.EventIterator;
import javax.jcr.observation.EventListener;
import javax.naming.Context;
import javax.naming.InitialContext;
import org.apache.jackrabbit.core.jndi.RegistryHelper;
import org.apache.jackrabbit.core.value.StringValue;
public class JCRTest {
private Repository repository;
public JCRTest(Repository repository) {
this.repository = repository;
}
public void execute() throws Exception {
Session session = this.repository.login(new
SimpleCredentials("", "".toCharArray()), null);
Node rn = session.getRootNode();
new ThreadObserver().start();
Node n = rn.addNode("node" + System.currentTimeMillis(),
"nt:unstructured");
n.setProperty("testprop", new StringValue("Hello, World."));
new ThreadDeleter().start();
session.save();
session.logout();
}
public static void main(String[] args) {
try {
String configFile = "repository/repository.xml";
String repHomeDir = "repository";
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.jackrabbit.core.jndi.provider.DummyInitialContextFactory");
env.put(Context.PROVIDER_URL, "localhost");
InitialContext ctx = new InitialContext(env);
RegistryHelper.registerRepository(ctx, "repo",
configFile, repHomeDir, true);
Repository r = (Repository) ctx.lookup("repo");
for (int i = 0; i < 20; i++) {
JCRTest test = new JCRTest(r);
test.execute();
}
} catch (Exception e){
e.printStackTrace();
}
}
public class ThreadObserver extends Thread {
public void run() {
System.out.println("Observing thread started");
try {
Session session = repository.login(new
SimpleCredentials("", "".toCharArray()));
EventListener el = new EventListener() {
public void onEvent(EventIterator
events) {
while (events.hasNext()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
Event e = events.nextEvent();
if (e.getType() ==
Event.NODE_ADDED) {
try {
System.out.println("Node
added : " + e.getPath());
} catch
(RepositoryException re) {
re.printStackTrace();
}
}
}
}
};
session.getWorkspace().getObservationManager().addEventListener(el,
Event.NODE_ADDED, "/", true, null, null, false);
session.save();
//Some time to listen
Thread.sleep(500);
session.logout();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class ThreadDeleter extends Thread {
public void run() {
System.out.println("Deleter thread started");
try {
Session session = repository.login(new
SimpleCredentials("", "".toCharArray()));
Node rn = session.getRootNode();
for (NodeIterator ni = rn.getNodes();
ni.hasNext(); ) {
Thread.sleep(100);
Node currentNode = ni.nextNode();
if
(currentNode.getName().startsWith("node")) {
currentNode.remove();
}
}
session.save();
session.logout();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Re: Problems with concurrent sessions
Posted by Nicolas Belisle <Ni...@bibl.ulaval.ca>.
Hi Oliver,
Thanks for your response.
>Thereforeall your threads have to have their own session
My code doesn't share any session between threads. Only the repository is
shared.
> [...] or you have to synchronize session access to avoid
>the exception you get (which is similar to ConcurrentModificationException
> thrown by standard SDK collections/iterators).
Then a solution (as you suggest) would like something like this :
synchronized( repository ) {
Session session = this.repository.login(new SimpleCredentials("",
"".toCharArray()), null);
//Do something
session.save();
session.logout();
}
What is the preferred approach ?
Also, if I'm responsible for preventing concurrrent sessions, then why is
there those multithreading issues in JIRA :
http://issues.apache.org/jira/browse/JCR-18 &
http://issues.apache.org/jira/browse/JCR-155 ?
Regards,
Nicolas
Le 18:37 2005-07-06, vous avez écrit:
>Nicolas,
>
>the JCR spec does not require a session to be thread-safe so you are on
>your own to take care for problems like the one you describe. Therefore
>all your threads have to have their own session or you have to
>synchronize session access to avoid the exception you get (which is
>similar to ConcurrentModificationException thrown by standard SDK
>collections/iterators).
>
>Oliver
>
>
>Nicolas Belisle wrote:
>
> > Hi,
> >
> > I'm a Jackrabbit newbie. I'm doing some tests with the tool and I'm
> > having problem with concurrent sessions.
> >
> > I have joined my (simple) test class (JCRTest) and the exception I get
> > from running it.
> >
> > Anyone had similar problems ?
> >
> > Regards,
> >
> > Nicolas
> >
> >
> > -->The exception report:
> > java.util.NoSuchElementException: cdeab285-fdbc-4af3-918a-bf4316a29276
> > at
> > org.apache.jackrabbit.core.LazyItemIterator.next(LazyItemIterator.java:157)
> >
> > at
> >
> org.apache.jackrabbit.core.LazyItemIterator.nextNode(LazyItemIterator.java:98)
> >
> > at app.JCRTest$ThreadDeleter.run(JCRTest.java:107)
> >
> > -->The class:
> > package app;
> >
> > import java.util.Hashtable;
> >
> > import javax.jcr.Node;
> > import javax.jcr.NodeIterator;
> > import javax.jcr.Repository;
> > import javax.jcr.RepositoryException;
> > import javax.jcr.Session;
> > import javax.jcr.SimpleCredentials;
> > import javax.jcr.observation.Event;
> > import javax.jcr.observation.EventIterator;
> > import javax.jcr.observation.EventListener;
> > import javax.naming.Context;
> > import javax.naming.InitialContext;
> >
> > import org.apache.jackrabbit.core.jndi.RegistryHelper;
> > import org.apache.jackrabbit.core.value.StringValue;
> >
> > public class JCRTest {
> >
> > private Repository repository;
> >
> > public JCRTest(Repository repository) {
> > this.repository = repository;
> > }
> >
> > public void execute() throws Exception {
> > Session session = this.repository.login(new
> > SimpleCredentials("", "".toCharArray()), null);
> >
> > Node rn = session.getRootNode();
> > new ThreadObserver().start();
> > Node n = rn.addNode("node" +
> > System.currentTimeMillis(), "nt:unstructured");
> > n.setProperty("testprop", new StringValue("Hello,
> > World."));
> > new ThreadDeleter().start();
> >
> > session.save();
> > session.logout();
> > }
> >
> > public static void main(String[] args) {
> > try {
> > String configFile = "repository/repository.xml";
> > String repHomeDir = "repository";
> >
> > Hashtable env = new Hashtable();
> > env.put(Context.INITIAL_CONTEXT_FACTORY,
> > "org.apache.jackrabbit.core.jndi.provider.DummyInitialContextFactory");
> > env.put(Context.PROVIDER_URL, "localhost");
> > InitialContext ctx = new InitialContext(env);
> >
> > RegistryHelper.registerRepository(ctx, "repo",
> > configFile, repHomeDir, true);
> > Repository r = (Repository) ctx.lookup("repo");
> >
> > for (int i = 0; i < 20; i++) {
> > JCRTest test = new JCRTest(r);
> > test.execute();
> > }
> >
> > } catch (Exception e){
> > e.printStackTrace();
> > }
> > }
> >
> > public class ThreadObserver extends Thread {
> > public void run() {
> > System.out.println("Observing thread started");
> > try {
> > Session session = repository.login(new
> > SimpleCredentials("", "".toCharArray()));
> > EventListener el = new EventListener() {
> > public void
> > onEvent(EventIterator events) {
> > while
> > (events.hasNext()) {
> > try {
> > Thread.sleep(100);
> > } catch (InterruptedException e) {}
> >
> > Event e = events.nextEvent();
> > if
> > (e.getType() == Event.NODE_ADDED) {
> > try {
> >
> > System.out.println("Node added : " + e.getPath());
> > }
> > catch (RepositoryException re) {
> >
> > re.printStackTrace();
> > }
> > }
> > }
> > }
> > };
> >
> > session.getWorkspace().getObservationManager().addEventListener(el,
> > Event.NODE_ADDED, "/", true, null, null, false);
> > session.save();
> > //Some time to listen
> > Thread.sleep(500);
> > session.logout();
> > } catch (Exception e) {
> > e.printStackTrace();
> > }
> > }
> > }
> >
> > public class ThreadDeleter extends Thread {
> > public void run() {
> > System.out.println("Deleter thread started");
> > try {
> > Session session = repository.login(new
> > SimpleCredentials("", "".toCharArray()));
> >
> > Node rn = session.getRootNode();
> > for (NodeIterator ni = rn.getNodes();
> > ni.hasNext(); ) {
> > Thread.sleep(100);
> > Node currentNode = ni.nextNode();
> > if
> > (currentNode.getName().startsWith("node")) {
> > currentNode.remove();
> > }
> > }
> > session.save();
> > session.logout();
> >
> > } catch (Exception e) {
> > e.printStackTrace();
> > }
> > }
> > }
> > }
> >
>
>
>--
>Oliver Rossmueller
>Software Engineer and IT-Consultant
>Hamburg, Germany
>http://www.rossmueller.com
Re: Problems with concurrent sessions
Posted by Oliver Rossmueller <ol...@tuxerra.com>.
Nicolas,
the JCR spec does not require a session to be thread-safe so you are on
your own to take care for problems like the one you describe. Therefore
all your threads have to have their own session or you have to
synchronize session access to avoid the exception you get (which is
similar to ConcurrentModificationException thrown by standard SDK
collections/iterators).
Oliver
Nicolas Belisle wrote:
> Hi,
>
> I'm a Jackrabbit newbie. I'm doing some tests with the tool and I'm
> having problem with concurrent sessions.
>
> I have joined my (simple) test class (JCRTest) and the exception I get
> from running it.
>
> Anyone had similar problems ?
>
> Regards,
>
> Nicolas
>
>
> -->The exception report:
> java.util.NoSuchElementException: cdeab285-fdbc-4af3-918a-bf4316a29276
> at
> org.apache.jackrabbit.core.LazyItemIterator.next(LazyItemIterator.java:157)
>
> at
> org.apache.jackrabbit.core.LazyItemIterator.nextNode(LazyItemIterator.java:98)
>
> at app.JCRTest$ThreadDeleter.run(JCRTest.java:107)
>
> -->The class:
> package app;
>
> import java.util.Hashtable;
>
> import javax.jcr.Node;
> import javax.jcr.NodeIterator;
> import javax.jcr.Repository;
> import javax.jcr.RepositoryException;
> import javax.jcr.Session;
> import javax.jcr.SimpleCredentials;
> import javax.jcr.observation.Event;
> import javax.jcr.observation.EventIterator;
> import javax.jcr.observation.EventListener;
> import javax.naming.Context;
> import javax.naming.InitialContext;
>
> import org.apache.jackrabbit.core.jndi.RegistryHelper;
> import org.apache.jackrabbit.core.value.StringValue;
>
> public class JCRTest {
>
> private Repository repository;
>
> public JCRTest(Repository repository) {
> this.repository = repository;
> }
>
> public void execute() throws Exception {
> Session session = this.repository.login(new
> SimpleCredentials("", "".toCharArray()), null);
>
> Node rn = session.getRootNode();
> new ThreadObserver().start();
> Node n = rn.addNode("node" +
> System.currentTimeMillis(), "nt:unstructured");
> n.setProperty("testprop", new StringValue("Hello,
> World."));
> new ThreadDeleter().start();
>
> session.save();
> session.logout();
> }
>
> public static void main(String[] args) {
> try {
> String configFile = "repository/repository.xml";
> String repHomeDir = "repository";
>
> Hashtable env = new Hashtable();
> env.put(Context.INITIAL_CONTEXT_FACTORY,
> "org.apache.jackrabbit.core.jndi.provider.DummyInitialContextFactory");
> env.put(Context.PROVIDER_URL, "localhost");
> InitialContext ctx = new InitialContext(env);
>
> RegistryHelper.registerRepository(ctx, "repo",
> configFile, repHomeDir, true);
> Repository r = (Repository) ctx.lookup("repo");
>
> for (int i = 0; i < 20; i++) {
> JCRTest test = new JCRTest(r);
> test.execute();
> }
>
> } catch (Exception e){
> e.printStackTrace();
> }
> }
>
> public class ThreadObserver extends Thread {
> public void run() {
> System.out.println("Observing thread started");
> try {
> Session session = repository.login(new
> SimpleCredentials("", "".toCharArray()));
> EventListener el = new EventListener() {
> public void
> onEvent(EventIterator events) {
> while
> (events.hasNext()) {
> try {
> Thread.sleep(100);
> } catch (InterruptedException e) {}
>
> Event e = events.nextEvent();
> if
> (e.getType() == Event.NODE_ADDED) {
> try {
>
> System.out.println("Node added : " + e.getPath());
> }
> catch (RepositoryException re) {
>
> re.printStackTrace();
> }
> }
> }
> }
> };
>
> session.getWorkspace().getObservationManager().addEventListener(el,
> Event.NODE_ADDED, "/", true, null, null, false);
> session.save();
> //Some time to listen
> Thread.sleep(500);
> session.logout();
> } catch (Exception e) {
> e.printStackTrace();
> }
> }
> }
>
> public class ThreadDeleter extends Thread {
> public void run() {
> System.out.println("Deleter thread started");
> try {
> Session session = repository.login(new
> SimpleCredentials("", "".toCharArray()));
>
> Node rn = session.getRootNode();
> for (NodeIterator ni = rn.getNodes();
> ni.hasNext(); ) {
> Thread.sleep(100);
> Node currentNode = ni.nextNode();
> if
> (currentNode.getName().startsWith("node")) {
> currentNode.remove();
> }
> }
> session.save();
> session.logout();
>
> } catch (Exception e) {
> e.printStackTrace();
> }
> }
> }
> }
>
--
Oliver Rossmueller
Software Engineer and IT-Consultant
Hamburg, Germany
http://www.rossmueller.com
Re: Problems with concurrent sessions
Posted by Julien Viet <ju...@jboss.org>.
the spec does not define isolation between concurrent sessions, you can
read chapter "Reflecting Item State" 7.1.2
it is left up to the vendor, so the question is rather : how is
isolation implemented in jackrabbit ?
it would be good to have clarifications indeed about how isolation is
achieved is jackrabbit and if it is configurable
somewhere.
Nicolas Belisle wrote:
> Hi,
>
> Thanks for your response.
>
> Before posting a jira issue, I would like to make a few things clear.
>
> What is the session isolation level [ref.
> http://www.unix.org.ua/orelly/java-ent/ebeans/ch08_03.htm] by default
> ? Is it configurable ? At least, Jackrabbit seems to prevent dirty
> reads (determined from tests). Is there any documentation regarding
> isolation level ? I think it is a very important topic...
>
> Example :
> A : open session
> A : read nodes "test" & "test2"
> B : open session
> A : delete nodes "test" & "test2"
> B : save session //WHAT SHOULD HAPPEN ??
> B : logout
> A : read nodes "test" & "test2" //WHAT SHOULD HAPPEN ??
> A : logout
>
> The answers differs depending on the isolation level.
>
> Also, does the test cases cover concurrent sessions ?
>
> Regards,
>
> Nicolas
>
>
> Le 05:33 2005-07-07, vous avez écrit:
>
>> hi nicolas
>>
>> On 7/6/05, Nicolas Belisle <Ni...@bibl.ulaval.ca> wrote:
>> > Hi,
>> >
>> > I'm a Jackrabbit newbie. I'm doing some tests with the tool and I'm
>> having
>> > problem with concurrent sessions.
>> >
>> > I have joined my (simple) test class (JCRTest) and the exception I
>> get from
>> > running it.
>> >
>> > Anyone had similar problems ?
>>
>> this seems to be a bug, please post a jira issue.
>>
>> thanks
>> stefan
>>
>> >
>> > Regards,
>> >
>> > Nicolas
>> >
>> >
>> > -->The exception report:
>> > java.util.NoSuchElementException: cdeab285-fdbc-4af3-918a-bf4316a29276
>> > at
>> org.apache.jackrabbit.core.LazyItemIterator.next(LazyItemIterator.java:157)
>>
>> > at
>> >
>> org.apache.jackrabbit.core.LazyItemIterator.nextNode(LazyItemIterator.java:98)
>>
>> > at app.JCRTest$ThreadDeleter.run(JCRTest.java:107)
>> >
>> > -->The class:
>> > package app;
>> >
>> > import java.util.Hashtable;
>> >
>> > import javax.jcr.Node;
>> > import javax.jcr.NodeIterator;
>> > import javax.jcr.Repository;
>> > import javax.jcr.RepositoryException;
>> > import javax.jcr.Session;
>> > import javax.jcr.SimpleCredentials;
>> > import javax.jcr.observation.Event;
>> > import javax.jcr.observation.EventIterator;
>> > import javax.jcr.observation.EventListener;
>> > import javax.naming.Context;
>> > import javax.naming.InitialContext;
>> >
>> > import org.apache.jackrabbit.core.jndi.RegistryHelper;
>> > import org.apache.jackrabbit.core.value.StringValue;
>> >
>> > public class JCRTest {
>> >
>> > private Repository repository;
>> >
>> > public JCRTest(Repository repository) {
>> > this.repository = repository;
>> > }
>> >
>> > public void execute() throws Exception {
>> > Session session = this.repository.login(new
>> > SimpleCredentials("", "".toCharArray()), null);
>> >
>> > Node rn = session.getRootNode();
>> > new ThreadObserver().start();
>> > Node n = rn.addNode("node" +
>> System.currentTimeMillis(),
>> > "nt:unstructured");
>> > n.setProperty("testprop", new StringValue("Hello,
>> World."));
>> > new ThreadDeleter().start();
>> >
>> > session.save();
>> > session.logout();
>> > }
>> >
>> > public static void main(String[] args) {
>> > try {
>> > String configFile =
>> "repository/repository.xml";
>> > String repHomeDir = "repository";
>> >
>> > Hashtable env = new Hashtable();
>> > env.put(Context.INITIAL_CONTEXT_FACTORY,
>> >
>> "org.apache.jackrabbit.core.jndi.provider.DummyInitialContextFactory");
>> > env.put(Context.PROVIDER_URL, "localhost");
>> > InitialContext ctx = new InitialContext(env);
>> >
>> > RegistryHelper.registerRepository(ctx,
>> "repo",
>> > configFile, repHomeDir, true);
>> > Repository r = (Repository)
>> ctx.lookup("repo");
>> >
>> > for (int i = 0; i < 20; i++) {
>> > JCRTest test = new JCRTest(r);
>> > test.execute();
>> > }
>> >
>> > } catch (Exception e){
>> > e.printStackTrace();
>> > }
>> > }
>> >
>> > public class ThreadObserver extends Thread {
>> > public void run() {
>> > System.out.println("Observing thread
>> started");
>> > try {
>> > Session session =
>> repository.login(new
>> > SimpleCredentials("", "".toCharArray()));
>> > EventListener el = new
>> EventListener() {
>> > public void
>> onEvent(EventIterator
>> > events) {
>> > while
>> (events.hasNext()) {
>> > try {
>> > Thread.sleep(100);
>> > } catch (InterruptedException
>> e) {}
>> >
>> > Event e = events.nextEvent();
>> > if
>> (e.getType() ==
>> > Event.NODE_ADDED) {
>> > try {
>> > System.out.println("Node
>> > added : " + e.getPath());
>> > }
>> catch
>> > (RepositoryException re) {
>> > re.printStackTrace();
>> > }
>> > }
>> >
>> > }
>> > }
>> > };
>> > session.getWorkspace().getObservationManager().addEventListener(el,
>> > Event.NODE_ADDED, "/", true, null, null, false);
>> > session.save();
>> > //Some time to listen
>> > Thread.sleep(500);
>> > session.logout();
>> > } catch (Exception e) {
>> > e.printStackTrace();
>> > }
>> > }
>> > }
>> >
>> > public class ThreadDeleter extends Thread {
>> > public void run() {
>> > System.out.println("Deleter thread started");
>> > try {
>> > Session session =
>> repository.login(new
>> > SimpleCredentials("", "".toCharArray()));
>> >
>> > Node rn = session.getRootNode();
>> > for (NodeIterator ni = rn.getNodes();
>> > ni.hasNext(); ) {
>> > Thread.sleep(100);
>> > Node currentNode =
>> ni.nextNode();
>> > if
>> > (currentNode.getName().startsWith("node")) {
>> > currentNode.remove();
>> > }
>> > }
>> > session.save();
>> > session.logout();
>> >
>> > } catch (Exception e) {
>> > e.printStackTrace();
>> > }
>> > }
>> > }
>> > }
>> >
>> >
>
>
>
--
Julien Viet
JBoss Portal Lead Developer
Re: Problems with concurrent sessions
Posted by Robert Ritchy <ro...@jarbo.com>.
Thanks - I did create the beginning of an examples page based old email
exchanges: http://wiki.apache.org/jackrabbit/ExamplesPage
If anyone out there has something to add; please feel free to contribute
your own example. In general, try to avoid examples explicitly covered
in the spec or the main Jackrabbit site.
-Robert
Roy T. Fielding wrote:
> On Jul 8, 2005, at 9:33 AM, Robert Ritchy wrote:
>
>> Summary information like this would be perfect for your wiki
>> (http://wiki.apache.org/jackrabbit). Are non-commiters encouraged to
>> sign up and contribute to the wiki? If so, I'd also like to start a
>> Best Practice (or maybe just 'Examples') Page.
>
>
> Yes, that is the only reason we have a wiki -- committers could
> just commit it to the docs. Go for it.
>
> ....Roy
>
>
Re: Problems with concurrent sessions
Posted by "Roy T. Fielding" <fi...@gbiv.com>.
On Jul 8, 2005, at 9:33 AM, Robert Ritchy wrote:
> Summary information like this would be perfect for your wiki
> (http://wiki.apache.org/jackrabbit). Are non-commiters encouraged to
> sign up and contribute to the wiki? If so, I'd also like to start a
> Best Practice (or maybe just 'Examples') Page.
Yes, that is the only reason we have a wiki -- committers could
just commit it to the docs. Go for it.
....Roy
Re: Problems with concurrent sessions
Posted by Robert Ritchy <ro...@jarbo.com>.
Stefan Guggisberg wrote:
>
>
>dominique roughly sketched the 3 layers (transient, local, shared)
>involved in jackrabbit's implementation of the 'read committed' isolation level
>in an earlier thread:
>http://article.gmane.org/gmane.comp.apache.jackrabbit.devel/1223
>
>cheers
>stefan
>
>
>
Summary information like this would be perfect for your wiki
(http://wiki.apache.org/jackrabbit). Are non-commiters encouraged to
sign up and contribute to the wiki? If so, I'd also like to start a
Best Practice (or maybe just 'Examples') Page.
-robert
Re: Problems with concurrent sessions
Posted by Stefan Guggisberg <st...@gmail.com>.
On 7/8/05, Marcel Reutegger <ma...@gmx.net> wrote:
> Hi Nicolas,
>
> Nicolas Belisle wrote:
> > What is the session isolation level [ref.
> > http://www.unix.org.ua/orelly/java-ent/ebeans/ch08_03.htm] by default ?
>
> the default is read commited.
>
> > Is it configurable ?
>
> no, but you may use locks to further control isolation level.
>
> a session in jackrabbit will see changes as soon as they are committed.
> you may however use locks to achieve a higher isolation level.
> successfully setting a lock on a node will give you repeatable read for
> that subtree. if you do that on the root node you get serializable
> isolation level.
>
> > At least, Jackrabbit seems to prevent dirty reads
> > (determined from tests). Is there any documentation regarding isolation
> > level ? I think it is a very important topic...
>
> Unfortunately there is currently no documentation on isolation levels,
> but this thread may be a good starting point to collect all relevant
> information and then put it into a nice xdoc or wiki page.
dominique roughly sketched the 3 layers (transient, local, shared)
involved in jackrabbit's implementation of the 'read committed' isolation level
in an earlier thread:
http://article.gmane.org/gmane.comp.apache.jackrabbit.devel/1223
cheers
stefan
>
> > Example :
> > A : open session
> > A : read nodes "test" & "test2"
> > B : open session
> > A : delete nodes "test" & "test2"
> > B : save session //WHAT SHOULD HAPPEN ??
> > B : logout
> > A : read nodes "test" & "test2" //WHAT SHOULD HAPPEN ??
> > A : logout
>
> I cannot quite follow you here. thread B seems to save a session without
> having made changes to it? that means 'B : save session' is basically no
> op. But I guess that's not what you want to know...
>
> regards
> marcel
>
Re: Problems with concurrent sessions
Posted by Marcel Reutegger <ma...@gmx.net>.
Hi Nicolas,
Nicolas Belisle wrote:
> Hi Marcel,
>
> If all three tryLock() fail, the event is certainly active. The event is
> then waiting for an ACTIVE lock to be released (the last tryLock()
> confirms that fact). The event is not cancelled/removed until the lock
> is obtained. Then doInTransaction() is called.
>
> The only way I see to stop the transaction from executing is to stop the
> server...
>
> Am I missing something ?
you are right. the listener will still be active when a tryLock() in
onEvent() fails. and will then be called again on the next unlock event.
sorry about the confusion I may have caused...
regards
marcel
Re: Problems with concurrent sessions
Posted by Nicolas Belisle <Ni...@bibl.ulaval.ca>.
Hi Marcel,
If all three tryLock() fail, the event is certainly active. The event is
then waiting for an ACTIVE lock to be released (the last tryLock() confirms
that fact). The event is not cancelled/removed until the lock is obtained.
Then doInTransaction() is called.
The only way I see to stop the transaction from executing is to stop the
server...
Am I missing something ?
Regards,
Nicolas
Le 05:54 2005-07-19, vous avez écrit:
>Hi Nicolas,
>
>Looks better now, but there are still cases where the doTransaction()
>method will not be called, though very unlikely: when all three tryLock()
>attempts fail. not very likely but theoretically possible...
>
>regards
> marcel
>
>Nicolas Belisle wrote:
>>Hi,
>>Thanks again for your comments.
>>Here's the second version of my template class. It should resolves the
>>concurrency issues you mentionned :
>>package app;
>>import javax.jcr.Credentials;
>>import javax.jcr.LoginException;
>>import javax.jcr.Node;
>>import javax.jcr.Repository;
>>import javax.jcr.RepositoryException;
>>import javax.jcr.Session;
>>import javax.jcr.lock.LockException;
>>import javax.jcr.observation.Event;
>>import javax.jcr.observation.EventIterator;
>>import javax.jcr.observation.EventListener;
>>public abstract class SerializableTemplate {
>> private Session session;
>> private Node scope;
>> private boolean done = false;
>> private EventListener el;
>> public SerializableTemplate(Repository repository, Credentials cr,
>> String scopePath) throws LoginException, RepositoryException {
>> session = repository.login(cr);
>> scope = session.getRootNode().getNode(scopePath);
>> //scope = session.getNodeByUUID(scope.getUUID());
>> }
>> public abstract void doInTransaction(Session session) throws
>> RepositoryException;
>> public void execute() throws RepositoryException {
>> if (tryLock()) {
>> return;
>> }
>> this.el = new EventListener() {
>> public void onEvent(EventIterator events) {
>> try {
>> tryLock();
>> } catch (RepositoryException e) {
>> throw new RuntimeException(e);
>> }
>> }
>> };
>>
>>session.getWorkspace().getObservationManager().addEventListener(el,
>>Event.PROPERTY_REMOVED, scope.getPath(), true, null, null, false);
>> //Try again, in case the lock is removed before observer could
>> be put in place
>> tryLock();
>> }
>> private synchronized boolean tryLock() throws RepositoryException {
>> try {
>> if (done) {
>> return false;
>> }
>> if (!scope.isLocked()) {
>> scope.lock(true, true);
>> try {
>> if (el != null) {
>>
>>session.getWorkspace().getObservationManager().removeEventListener(el);
>> }
>> doInTransaction(session);
>> } finally {
>> done = true;
>> if (session.isLive()) {
>> session.logout();
>> }
>> }
>> return true;
>> }
>> } catch (LockException e) {
>> e.printStackTrace();
>> }
>> return false;
>> }
>>}
>>Here's how to use it :
>>SerializableTemplate sTemplate = new SerializableTemplate(repository, new
>>SimpleCredentials("user", "password".toCharArray()), "node/path") {
>> //@Override
>> public void doInTransaction(Session session) throws
>> RepositoryException {
>> //Do your favorite transaction...
>> };
>>sTemplate.execute();
>>
>>For the constructor you suggested, I actually came up with a similiar
>>design at first, but found a problem with it : since the template class
>>might use an EventListener the class should be responsible for closing
>>the session (the EventListener can wait a while...). Else, the event
>>could be removed by the user before being invoked. That's the reason for
>>my ugly constructor.
>>I welcome your comments again...
>>Regards,
>>Nicolas
Re: Problems with concurrent sessions
Posted by Marcel Reutegger <ma...@gmx.net>.
Hi Nicolas,
Looks better now, but there are still cases where the doTransaction()
method will not be called, though very unlikely: when all three
tryLock() attempts fail. not very likely but theoretically possible...
regards
marcel
Nicolas Belisle wrote:
> Hi,
>
> Thanks again for your comments.
>
> Here's the second version of my template class. It should resolves the
> concurrency issues you mentionned :
>
> package app;
>
> import javax.jcr.Credentials;
> import javax.jcr.LoginException;
> import javax.jcr.Node;
> import javax.jcr.Repository;
> import javax.jcr.RepositoryException;
> import javax.jcr.Session;
> import javax.jcr.lock.LockException;
> import javax.jcr.observation.Event;
> import javax.jcr.observation.EventIterator;
> import javax.jcr.observation.EventListener;
>
> public abstract class SerializableTemplate {
>
> private Session session;
> private Node scope;
> private boolean done = false;
> private EventListener el;
>
> public SerializableTemplate(Repository repository, Credentials cr,
> String scopePath) throws LoginException, RepositoryException {
> session = repository.login(cr);
> scope = session.getRootNode().getNode(scopePath);
> //scope = session.getNodeByUUID(scope.getUUID());
> }
>
> public abstract void doInTransaction(Session session) throws
> RepositoryException;
>
> public void execute() throws RepositoryException {
> if (tryLock()) {
> return;
> }
>
> this.el = new EventListener() {
> public void onEvent(EventIterator events) {
> try {
> tryLock();
> } catch (RepositoryException e) {
> throw new RuntimeException(e);
> }
> }
> };
>
> session.getWorkspace().getObservationManager().addEventListener(el,
> Event.PROPERTY_REMOVED, scope.getPath(), true, null, null, false);
>
> //Try again, in case the lock is removed before observer could
> be put in place
> tryLock();
> }
>
> private synchronized boolean tryLock() throws RepositoryException {
> try {
> if (done) {
> return false;
> }
>
> if (!scope.isLocked()) {
> scope.lock(true, true);
> try {
> if (el != null) {
>
> session.getWorkspace().getObservationManager().removeEventListener(el);
> }
> doInTransaction(session);
> } finally {
> done = true;
> if (session.isLive()) {
> session.logout();
> }
> }
> return true;
> }
> } catch (LockException e) {
> e.printStackTrace();
> }
> return false;
> }
> }
>
> Here's how to use it :
>
> SerializableTemplate sTemplate = new SerializableTemplate(repository,
> new SimpleCredentials("user", "password".toCharArray()), "node/path") {
> //@Override
> public void doInTransaction(Session session) throws
> RepositoryException {
> //Do your favorite transaction...
> };
> sTemplate.execute();
>
>
> For the constructor you suggested, I actually came up with a similiar
> design at first, but found a problem with it : since the template class
> might use an EventListener the class should be responsible for closing
> the session (the EventListener can wait a while...). Else, the event
> could be removed by the user before being invoked. That's the reason for
> my ugly constructor.
>
> I welcome your comments again...
>
> Regards,
>
> Nicolas
>
Re: Problems with concurrent sessions
Posted by Nicolas Belisle <Ni...@bibl.ulaval.ca>.
Hi,
Thanks again for your comments.
Here's the second version of my template class. It should resolves the
concurrency issues you mentionned :
package app;
import javax.jcr.Credentials;
import javax.jcr.LoginException;
import javax.jcr.Node;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.lock.LockException;
import javax.jcr.observation.Event;
import javax.jcr.observation.EventIterator;
import javax.jcr.observation.EventListener;
public abstract class SerializableTemplate {
private Session session;
private Node scope;
private boolean done = false;
private EventListener el;
public SerializableTemplate(Repository repository, Credentials cr,
String scopePath) throws LoginException, RepositoryException {
session = repository.login(cr);
scope = session.getRootNode().getNode(scopePath);
//scope = session.getNodeByUUID(scope.getUUID());
}
public abstract void doInTransaction(Session session) throws
RepositoryException;
public void execute() throws RepositoryException {
if (tryLock()) {
return;
}
this.el = new EventListener() {
public void onEvent(EventIterator events) {
try {
tryLock();
} catch (RepositoryException e) {
throw new RuntimeException(e);
}
}
};
session.getWorkspace().getObservationManager().addEventListener(el,
Event.PROPERTY_REMOVED, scope.getPath(), true, null, null, false);
//Try again, in case the lock is removed before observer could be
put in place
tryLock();
}
private synchronized boolean tryLock() throws RepositoryException {
try {
if (done) {
return false;
}
if (!scope.isLocked()) {
scope.lock(true, true);
try {
if (el != null) {
session.getWorkspace().getObservationManager().removeEventListener(el);
}
doInTransaction(session);
} finally {
done = true;
if (session.isLive()) {
session.logout();
}
}
return true;
}
} catch (LockException e) {
e.printStackTrace();
}
return false;
}
}
Here's how to use it :
SerializableTemplate sTemplate = new SerializableTemplate(repository, new
SimpleCredentials("user", "password".toCharArray()), "node/path") {
//@Override
public void doInTransaction(Session session) throws
RepositoryException {
//Do your favorite transaction...
};
sTemplate.execute();
For the constructor you suggested, I actually came up with a similiar
design at first, but found a problem with it : since the template class
might use an EventListener the class should be responsible for closing the
session (the EventListener can wait a while...). Else, the event could be
removed by the user before being invoked. That's the reason for my ugly
constructor.
I welcome your comments again...
Regards,
Nicolas
Le 05:07 2005-07-15, vous avez écrit:
>Hi Nicolas,
>
>I can see where you are heading for ;)
>
>I think the constructor take the node that acts as the lock scope. It
>should provide all that is needed:
>
>public SerializableTemplate(Node scope) {
> session = scope.getSession();
> this.scope = scope;
>}
>
>you may then also ommit the session parameter in doInTransaction. And just
>as a minor improvement the method should be allowed to throw a
>RepositoryException.
>
>After checking whether a node is locked it is not guaranteed that you can
>then lock the node.
>A similar concurrency problem can arise when isLocked() returns true,
>between that call and the listener registration the node might get
>unlocked. so, you don't get an event for that and keep waiting.
>
>regards
> marcel
>
>Nicolas Belisle wrote:
>>I just thought about something like this (Note that I've only done a few
>>tests on that class.) :
>>public abstract class SerializableTemplate {
>> private Session session;
>> private Node scope;
>> public SerializableTemplate(Repository repository, Credentials cr,
>> String scopePath) throws LoginException, RepositoryException {
>> session = repository.login(cr);
>> scope = session.getRootNode().getNode(scopePath);
>> }
>> public abstract void doInTransaction(Session session);
>> public void execute() {
>> try {
>> if (!scope.isLocked()) {
>> scope.lock(true, true);
>> doInTransaction(session);
>> if (session.isLive()) {
>> session.logout();
>> }
>> } else {
>> EventListener el = new EventListener() {
>> public void onEvent(EventIterator events) {
>> try {
>> if (!scope.isLocked()) {
>> scope.lock(true, true);
>> doInTransaction(session);
>> if (session.isLive()) {
>> session.logout();
>> }
>> }
>> } catch (Exception e) {
>> throw new RuntimeException(e);
>> }
>> }
>> };
>>
>>session.getWorkspace().getObservationManager().addEventListener(el,
>>Event.PROPERTY_REMOVED, scope.getPath(), true, null, null, false);
>> }
>> } catch (Exception e) {
>> throw new RuntimeException(e);
>> }
>> }
>>}
>>I don't like a few things about that class, especially the constructor...
>>What do you think overall ?
>> > Again I think this always depends on the application on top of the
>> repository.
>> > Setting an isolation level as a global property does not seems to be
>> a good idea to me.
>>Well, if many applications need control of their isolation level, maybe
>>that feature should be implemented in one place or documented in a worked
>>example...
>>Regards,
>>Nicolas
Re: Problems with concurrent sessions
Posted by Marcel Reutegger <ma...@gmx.net>.
Hi Nicolas,
I can see where you are heading for ;)
I think the constructor take the node that acts as the lock scope. It
should provide all that is needed:
public SerializableTemplate(Node scope) {
session = scope.getSession();
this.scope = scope;
}
you may then also ommit the session parameter in doInTransaction. And
just as a minor improvement the method should be allowed to throw a
RepositoryException.
After checking whether a node is locked it is not guaranteed that you
can then lock the node.
A similar concurrency problem can arise when isLocked() returns true,
between that call and the listener registration the node might get
unlocked. so, you don't get an event for that and keep waiting.
regards
marcel
Nicolas Belisle wrote:
> I just thought about something like this (Note that I've only done a few
> tests on that class.) :
>
> public abstract class SerializableTemplate {
>
> private Session session;
> private Node scope;
>
> public SerializableTemplate(Repository repository, Credentials cr,
> String scopePath) throws LoginException, RepositoryException {
> session = repository.login(cr);
> scope = session.getRootNode().getNode(scopePath);
> }
>
> public abstract void doInTransaction(Session session);
>
> public void execute() {
> try {
> if (!scope.isLocked()) {
> scope.lock(true, true);
> doInTransaction(session);
> if (session.isLive()) {
> session.logout();
> }
> } else {
> EventListener el = new EventListener() {
> public void onEvent(EventIterator events) {
> try {
> if (!scope.isLocked()) {
> scope.lock(true, true);
> doInTransaction(session);
> if (session.isLive()) {
> session.logout();
> }
> }
> } catch (Exception e) {
> throw new RuntimeException(e);
> }
> }
> };
>
> session.getWorkspace().getObservationManager().addEventListener(el,
> Event.PROPERTY_REMOVED, scope.getPath(), true, null, null, false);
> }
> } catch (Exception e) {
> throw new RuntimeException(e);
> }
> }
> }
>
> I don't like a few things about that class, especially the constructor...
>
> What do you think overall ?
>
> > Again I think this always depends on the application on top of the
> repository.
> > Setting an isolation level as a global property does not seems to be
> a good idea to me.
>
> Well, if many applications need control of their isolation level, maybe
> that feature should be implemented in one place or documented in a
> worked example...
>
> Regards,
>
> Nicolas
>
Re: Problems with concurrent sessions
Posted by Nicolas Belisle <Ni...@bibl.ulaval.ca>.
Hi again,
> You probably want a lock call that is blocking and returns when the lock
could be retrieved.
> Locks in jsr-170 were primarily designed to lock nodes for a longer time
and not just while
> in a transaction. Maybe a blocking variant of the getLock() method is
something we should
> consider for the next jcr version.
I just thought about something like this (Note that I've only done a few
tests on that class.) :
import javax.jcr.Credentials;
import javax.jcr.LoginException;
import javax.jcr.Node;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.observation.Event;
import javax.jcr.observation.EventIterator;
import javax.jcr.observation.EventListener;
public abstract class SerializableTemplate {
private Session session;
private Node scope;
public SerializableTemplate(Repository repository, Credentials cr,
String scopePath) throws LoginException, RepositoryException {
session = repository.login(cr);
scope = session.getRootNode().getNode(scopePath);
}
public abstract void doInTransaction(Session session);
public void execute() {
try {
if (!scope.isLocked()) {
scope.lock(true, true);
doInTransaction(session);
if (session.isLive()) {
session.logout();
}
} else {
EventListener el = new EventListener() {
public void onEvent(EventIterator events) {
try {
if (!scope.isLocked()) {
scope.lock(true, true);
doInTransaction(session);
if (session.isLive()) {
session.logout();
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
};
session.getWorkspace().getObservationManager().addEventListener(el,
Event.PROPERTY_REMOVED, scope.getPath(), true, null, null, false);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
I don't like a few things about that class, especially the constructor...
What do you think overall ?
> Again I think this always depends on the application on top of the
repository.
> Setting an isolation level as a global property does not seems to be a
good idea to me.
Well, if many applications need control of their isolation level, maybe
that feature should be implemented in one place or documented in a worked
example...
Regards,
Nicolas
Le 12:34 2005-07-08, vous avez écrit:
>Hi Nicolas,
>
>Nicolas Belisle wrote:
>> > no, but you may use locks to further control isolation level.
>>There seems to be at least two problems with that approach :
>>- You will get a (unpleasant) javax.jcr.lock.LockException if another
>>transaction tries to lock the node (or a child).
>
>You probably want a lock call that is blocking and returns when the lock
>could be retrieved. Locks in jsr-170 were primarily designed to lock nodes
>for a longer time and not just while in a transaction. Maybe a blocking
>variant of the getLock() method is something we should consider for the
>next jcr version.
>
>>- It is not enforced even though it should be used everywhere, even for
>>only a read.
>
>This depends on the application. I don't think you have to lock the
>workspace when your applicaton is fine with an read-committed isolation level.
>
>>Sorry there was an error in my example. I should make more sense this way :
>>Example v.2 :
>>A : open session
>>A : read nodes "test" & "test2"
>>B : open session
>>B : delete nodes "test" & "test2"
>>B : save session //WHAT SHOULD HAPPEN ??
>>B : logout
>>A : read nodes "test" & "test2" //WHAT SHOULD HAPPEN ??
>>A : logout
>
>yeah, that definitively makes more sense ;)
>
>deleting the two nodes with Session B will be successful, unless session A
>has locked the two nodes.
>regarding the second attempt to read the two nodes with session A, this
>depends whether A already obtained references to the nodes or not.
>
>If A already has reference to those two nodes, calles that read the state
>of those nodes will throw an InvalidItemStateException. Indicating that
>the nodes do not exist anymore.
>
>If A tries to retrieve the nodes again. E.g. by calling getNodes() on the
>parent of the just deleted nodes. The returned NodeIterator will simply
>not contain the nodes anymore.
>
>>It would be useful if the isolation level could configured at the
>>repository level. So if your application needs better isolation or
>>performance you would only need to change the configuration and not your code.
>
>Again I think this always depends on the application on top of the
>repository. Setting an isolation level as a global property does not seems
>to be a good idea to me.
>
>>An implementation could (hypothetically) use locks in SessionImpl and use
>>events (when a session saves/logout) to prevent throwing exceptions. A
>>queue would hold pending operations. However, we would have to be careful
>>about long lived sessions...
>
>I'm sorry, I cannot follow you here...
>
>regards
> marcel
Re: Problems with concurrent sessions
Posted by Marcel Reutegger <ma...@gmx.net>.
Hi Nicolas,
Nicolas Belisle wrote:
> > no, but you may use locks to further control isolation level.
>
> There seems to be at least two problems with that approach :
>
> - You will get a (unpleasant) javax.jcr.lock.LockException if another
> transaction tries to lock the node (or a child).
You probably want a lock call that is blocking and returns when the lock
could be retrieved. Locks in jsr-170 were primarily designed to lock
nodes for a longer time and not just while in a transaction. Maybe a
blocking variant of the getLock() method is something we should consider
for the next jcr version.
> - It is not enforced even though it should be used everywhere, even for
> only a read.
This depends on the application. I don't think you have to lock the
workspace when your applicaton is fine with an read-committed isolation
level.
> Sorry there was an error in my example. I should make more sense this way :
>
> Example v.2 :
> A : open session
> A : read nodes "test" & "test2"
> B : open session
> B : delete nodes "test" & "test2"
> B : save session //WHAT SHOULD HAPPEN ??
> B : logout
> A : read nodes "test" & "test2" //WHAT SHOULD HAPPEN ??
> A : logout
yeah, that definitively makes more sense ;)
deleting the two nodes with Session B will be successful, unless session
A has locked the two nodes.
regarding the second attempt to read the two nodes with session A, this
depends whether A already obtained references to the nodes or not.
If A already has reference to those two nodes, calles that read the
state of those nodes will throw an InvalidItemStateException. Indicating
that the nodes do not exist anymore.
If A tries to retrieve the nodes again. E.g. by calling getNodes() on
the parent of the just deleted nodes. The returned NodeIterator will
simply not contain the nodes anymore.
> It would be useful if the isolation level could configured at the
> repository level. So if your application needs better isolation or
> performance you would only need to change the configuration and not your
> code.
Again I think this always depends on the application on top of the
repository. Setting an isolation level as a global property does not
seems to be a good idea to me.
> An implementation could (hypothetically) use locks in SessionImpl and
> use events (when a session saves/logout) to prevent throwing exceptions.
> A queue would hold pending operations. However, we would have to be
> careful about long lived sessions...
I'm sorry, I cannot follow you here...
regards
marcel
Re: Problems with concurrent sessions
Posted by Nicolas Belisle <Ni...@bibl.ulaval.ca>.
Hi,
Many thanks for your response.
> no, but you may use locks to further control isolation level.
There seems to be at least two problems with that approach :
- You will get a (unpleasant) javax.jcr.lock.LockException if another
transaction tries to lock the node (or a child).
- It is not enforced even though it should be used everywhere, even for
only a read.
> I cannot quite follow you here. thread B seems to save a session without
having
> made changes to it? that means 'B : save session' is basically no op.
But I guess
> that's not what you want to know...
Sorry there was an error in my example. I should make more sense this way :
Example v.2 :
A : open session
A : read nodes "test" & "test2"
B : open session
B : delete nodes "test" & "test2"
B : save session //WHAT SHOULD HAPPEN ??
B : logout
A : read nodes "test" & "test2" //WHAT SHOULD HAPPEN ??
A : logout
It would be useful if the isolation level could configured at the
repository level. So if your application needs better isolation or
performance you would only need to change the configuration and not your code.
An implementation could (hypothetically) use locks in SessionImpl and use
events (when a session saves/logout) to prevent throwing exceptions. A
queue would hold pending operations. However, we would have to be careful
about long lived sessions...
What do you think ?
Regards,
Nicolas
Le 04:06 2005-07-08, vous avez écrit:
>Hi Nicolas,
>
>Nicolas Belisle wrote:
>>What is the session isolation level [ref.
>>http://www.unix.org.ua/orelly/java-ent/ebeans/ch08_03.htm] by default ?
>
>the default is read commited.
>
>>Is it configurable ?
>
>no, but you may use locks to further control isolation level.
>
>a session in jackrabbit will see changes as soon as they are committed.
>you may however use locks to achieve a higher isolation level.
>successfully setting a lock on a node will give you repeatable read for
>that subtree. if you do that on the root node you get serializable
>isolation level.
>
>>At least, Jackrabbit seems to prevent dirty reads (determined from
>>tests). Is there any documentation regarding isolation level ? I think it
>>is a very important topic...
>
>Unfortunately there is currently no documentation on isolation levels, but
>this thread may be a good starting point to collect all relevant
>information and then put it into a nice xdoc or wiki page.
>
>>Example :
>>A : open session
>>A : read nodes "test" & "test2"
>>B : open session
>>A : delete nodes "test" & "test2"
>>B : save session //WHAT SHOULD HAPPEN ??
>>B : logout
>>A : read nodes "test" & "test2" //WHAT SHOULD HAPPEN ??
>>A : logout
>
>I cannot quite follow you here. thread B seems to save a session without
>having made changes to it? that means 'B : save session' is basically no
>op. But I guess that's not what you want to know...
>
>regards
> marcel
Re: Problems with concurrent sessions
Posted by Marcel Reutegger <ma...@gmx.net>.
Hi Nicolas,
Nicolas Belisle wrote:
> What is the session isolation level [ref.
> http://www.unix.org.ua/orelly/java-ent/ebeans/ch08_03.htm] by default ?
the default is read commited.
> Is it configurable ?
no, but you may use locks to further control isolation level.
a session in jackrabbit will see changes as soon as they are committed.
you may however use locks to achieve a higher isolation level.
successfully setting a lock on a node will give you repeatable read for
that subtree. if you do that on the root node you get serializable
isolation level.
> At least, Jackrabbit seems to prevent dirty reads
> (determined from tests). Is there any documentation regarding isolation
> level ? I think it is a very important topic...
Unfortunately there is currently no documentation on isolation levels,
but this thread may be a good starting point to collect all relevant
information and then put it into a nice xdoc or wiki page.
> Example :
> A : open session
> A : read nodes "test" & "test2"
> B : open session
> A : delete nodes "test" & "test2"
> B : save session //WHAT SHOULD HAPPEN ??
> B : logout
> A : read nodes "test" & "test2" //WHAT SHOULD HAPPEN ??
> A : logout
I cannot quite follow you here. thread B seems to save a session without
having made changes to it? that means 'B : save session' is basically no
op. But I guess that's not what you want to know...
regards
marcel
Re: Problems with concurrent sessions
Posted by Nicolas Belisle <Ni...@bibl.ulaval.ca>.
Hi,
Thanks for your response.
Before posting a jira issue, I would like to make a few things clear.
What is the session isolation level [ref.
http://www.unix.org.ua/orelly/java-ent/ebeans/ch08_03.htm] by default ? Is
it configurable ? At least, Jackrabbit seems to prevent dirty reads
(determined from tests). Is there any documentation regarding isolation
level ? I think it is a very important topic...
Example :
A : open session
A : read nodes "test" & "test2"
B : open session
A : delete nodes "test" & "test2"
B : save session //WHAT SHOULD HAPPEN ??
B : logout
A : read nodes "test" & "test2" //WHAT SHOULD HAPPEN ??
A : logout
The answers differs depending on the isolation level.
Also, does the test cases cover concurrent sessions ?
Regards,
Nicolas
Le 05:33 2005-07-07, vous avez écrit:
>hi nicolas
>
>On 7/6/05, Nicolas Belisle <Ni...@bibl.ulaval.ca> wrote:
> > Hi,
> >
> > I'm a Jackrabbit newbie. I'm doing some tests with the tool and I'm having
> > problem with concurrent sessions.
> >
> > I have joined my (simple) test class (JCRTest) and the exception I get from
> > running it.
> >
> > Anyone had similar problems ?
>
>this seems to be a bug, please post a jira issue.
>
>thanks
>stefan
>
> >
> > Regards,
> >
> > Nicolas
> >
> >
> > -->The exception report:
> > java.util.NoSuchElementException: cdeab285-fdbc-4af3-918a-bf4316a29276
> > at
> org.apache.jackrabbit.core.LazyItemIterator.next(LazyItemIterator.java:157)
> > at
> >
> org.apache.jackrabbit.core.LazyItemIterator.nextNode(LazyItemIterator.java:98)
> > at app.JCRTest$ThreadDeleter.run(JCRTest.java:107)
> >
> > -->The class:
> > package app;
> >
> > import java.util.Hashtable;
> >
> > import javax.jcr.Node;
> > import javax.jcr.NodeIterator;
> > import javax.jcr.Repository;
> > import javax.jcr.RepositoryException;
> > import javax.jcr.Session;
> > import javax.jcr.SimpleCredentials;
> > import javax.jcr.observation.Event;
> > import javax.jcr.observation.EventIterator;
> > import javax.jcr.observation.EventListener;
> > import javax.naming.Context;
> > import javax.naming.InitialContext;
> >
> > import org.apache.jackrabbit.core.jndi.RegistryHelper;
> > import org.apache.jackrabbit.core.value.StringValue;
> >
> > public class JCRTest {
> >
> > private Repository repository;
> >
> > public JCRTest(Repository repository) {
> > this.repository = repository;
> > }
> >
> > public void execute() throws Exception {
> > Session session = this.repository.login(new
> > SimpleCredentials("", "".toCharArray()), null);
> >
> > Node rn = session.getRootNode();
> > new ThreadObserver().start();
> > Node n = rn.addNode("node" + System.currentTimeMillis(),
> > "nt:unstructured");
> > n.setProperty("testprop", new StringValue("Hello,
> World."));
> > new ThreadDeleter().start();
> >
> > session.save();
> > session.logout();
> > }
> >
> > public static void main(String[] args) {
> > try {
> > String configFile = "repository/repository.xml";
> > String repHomeDir = "repository";
> >
> > Hashtable env = new Hashtable();
> > env.put(Context.INITIAL_CONTEXT_FACTORY,
> > "org.apache.jackrabbit.core.jndi.provider.DummyInitialContextFactory");
> > env.put(Context.PROVIDER_URL, "localhost");
> > InitialContext ctx = new InitialContext(env);
> >
> > RegistryHelper.registerRepository(ctx, "repo",
> > configFile, repHomeDir, true);
> > Repository r = (Repository) ctx.lookup("repo");
> >
> > for (int i = 0; i < 20; i++) {
> > JCRTest test = new JCRTest(r);
> > test.execute();
> > }
> >
> > } catch (Exception e){
> > e.printStackTrace();
> > }
> > }
> >
> > public class ThreadObserver extends Thread {
> > public void run() {
> > System.out.println("Observing thread started");
> > try {
> > Session session = repository.login(new
> > SimpleCredentials("", "".toCharArray()));
> > EventListener el = new EventListener() {
> > public void onEvent(EventIterator
> > events) {
> > while (events.hasNext()) {
> > try {
> > Thread.sleep(100);
> > } catch (InterruptedException e) {}
> >
> > Event e = events.nextEvent();
> > if (e.getType() ==
> > Event.NODE_ADDED) {
> > try {
> >
> System.out.println("Node
> > added : " + e.getPath());
> > } catch
> > (RepositoryException re) {
> >
> re.printStackTrace();
> > }
> > }
> >
> > }
> > }
> > };
> >
> session.getWorkspace().getObservationManager().addEventListener(el,
> > Event.NODE_ADDED, "/", true, null, null, false);
> > session.save();
> > //Some time to listen
> > Thread.sleep(500);
> > session.logout();
> > } catch (Exception e) {
> > e.printStackTrace();
> > }
> > }
> > }
> >
> > public class ThreadDeleter extends Thread {
> > public void run() {
> > System.out.println("Deleter thread started");
> > try {
> > Session session = repository.login(new
> > SimpleCredentials("", "".toCharArray()));
> >
> > Node rn = session.getRootNode();
> > for (NodeIterator ni = rn.getNodes();
> > ni.hasNext(); ) {
> > Thread.sleep(100);
> > Node currentNode = ni.nextNode();
> > if
> > (currentNode.getName().startsWith("node")) {
> > currentNode.remove();
> > }
> > }
> > session.save();
> > session.logout();
> >
> > } catch (Exception e) {
> > e.printStackTrace();
> > }
> > }
> > }
> > }
> >
> >
Re: Problems with concurrent sessions
Posted by Stefan Guggisberg <st...@gmail.com>.
On 7/7/05, Walter Raboch <wr...@ingen.at> wrote:
> Hi,
>
> I experience similar problems here. I start some threads with one
> session per thread and make random read/write calls with random sleep
> periods between them. As soon as there are 2 pending saves at the same
> time, I get NoSuchElementExceptions.
please post a jira issue and include a stack trace and detailed instructions
how to reproduce the problem.
thanks
stefan
>
> cheers,
>
> Walter
>
>
Re: Problems with concurrent sessions
Posted by Walter Raboch <wr...@ingen.at>.
Hi,
I experience similar problems here. I start some threads with one
session per thread and make random read/write calls with random sleep
periods between them. As soon as there are 2 pending saves at the same
time, I get NoSuchElementExceptions.
cheers,
Walter
Re: Problems with concurrent sessions
Posted by Stefan Guggisberg <st...@gmail.com>.
hi nicolas
On 7/6/05, Nicolas Belisle <Ni...@bibl.ulaval.ca> wrote:
> Hi,
>
> I'm a Jackrabbit newbie. I'm doing some tests with the tool and I'm having
> problem with concurrent sessions.
>
> I have joined my (simple) test class (JCRTest) and the exception I get from
> running it.
>
> Anyone had similar problems ?
this seems to be a bug, please post a jira issue.
thanks
stefan
>
> Regards,
>
> Nicolas
>
>
> -->The exception report:
> java.util.NoSuchElementException: cdeab285-fdbc-4af3-918a-bf4316a29276
> at org.apache.jackrabbit.core.LazyItemIterator.next(LazyItemIterator.java:157)
> at
> org.apache.jackrabbit.core.LazyItemIterator.nextNode(LazyItemIterator.java:98)
> at app.JCRTest$ThreadDeleter.run(JCRTest.java:107)
>
> -->The class:
> package app;
>
> import java.util.Hashtable;
>
> import javax.jcr.Node;
> import javax.jcr.NodeIterator;
> import javax.jcr.Repository;
> import javax.jcr.RepositoryException;
> import javax.jcr.Session;
> import javax.jcr.SimpleCredentials;
> import javax.jcr.observation.Event;
> import javax.jcr.observation.EventIterator;
> import javax.jcr.observation.EventListener;
> import javax.naming.Context;
> import javax.naming.InitialContext;
>
> import org.apache.jackrabbit.core.jndi.RegistryHelper;
> import org.apache.jackrabbit.core.value.StringValue;
>
> public class JCRTest {
>
> private Repository repository;
>
> public JCRTest(Repository repository) {
> this.repository = repository;
> }
>
> public void execute() throws Exception {
> Session session = this.repository.login(new
> SimpleCredentials("", "".toCharArray()), null);
>
> Node rn = session.getRootNode();
> new ThreadObserver().start();
> Node n = rn.addNode("node" + System.currentTimeMillis(),
> "nt:unstructured");
> n.setProperty("testprop", new StringValue("Hello, World."));
> new ThreadDeleter().start();
>
> session.save();
> session.logout();
> }
>
> public static void main(String[] args) {
> try {
> String configFile = "repository/repository.xml";
> String repHomeDir = "repository";
>
> Hashtable env = new Hashtable();
> env.put(Context.INITIAL_CONTEXT_FACTORY,
> "org.apache.jackrabbit.core.jndi.provider.DummyInitialContextFactory");
> env.put(Context.PROVIDER_URL, "localhost");
> InitialContext ctx = new InitialContext(env);
>
> RegistryHelper.registerRepository(ctx, "repo",
> configFile, repHomeDir, true);
> Repository r = (Repository) ctx.lookup("repo");
>
> for (int i = 0; i < 20; i++) {
> JCRTest test = new JCRTest(r);
> test.execute();
> }
>
> } catch (Exception e){
> e.printStackTrace();
> }
> }
>
> public class ThreadObserver extends Thread {
> public void run() {
> System.out.println("Observing thread started");
> try {
> Session session = repository.login(new
> SimpleCredentials("", "".toCharArray()));
> EventListener el = new EventListener() {
> public void onEvent(EventIterator
> events) {
> while (events.hasNext()) {
> try {
> Thread.sleep(100);
> } catch (InterruptedException e) {}
>
> Event e = events.nextEvent();
> if (e.getType() ==
> Event.NODE_ADDED) {
> try {
> System.out.println("Node
> added : " + e.getPath());
> } catch
> (RepositoryException re) {
> re.printStackTrace();
> }
> }
>
> }
> }
> };
> session.getWorkspace().getObservationManager().addEventListener(el,
> Event.NODE_ADDED, "/", true, null, null, false);
> session.save();
> //Some time to listen
> Thread.sleep(500);
> session.logout();
> } catch (Exception e) {
> e.printStackTrace();
> }
> }
> }
>
> public class ThreadDeleter extends Thread {
> public void run() {
> System.out.println("Deleter thread started");
> try {
> Session session = repository.login(new
> SimpleCredentials("", "".toCharArray()));
>
> Node rn = session.getRootNode();
> for (NodeIterator ni = rn.getNodes();
> ni.hasNext(); ) {
> Thread.sleep(100);
> Node currentNode = ni.nextNode();
> if
> (currentNode.getName().startsWith("node")) {
> currentNode.remove();
> }
> }
> session.save();
> session.logout();
>
> } catch (Exception e) {
> e.printStackTrace();
> }
> }
> }
> }
>
>