-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 13 posts ] 
Author Message
 Post subject: Proper use of disconnect()/reconnect()
PostPosted: Thu Jan 08, 2004 9:02 am 
Newbie

Joined: Thu Jan 08, 2004 8:33 am
Posts: 9
Location: Abbekerk, the Netherlands
My code is not what it should be currently and I want to fix it. I am planning on changing it to the code below. Please tell me whether this is the right way to do it or whether this will introduce problems.

I am trying to make a single transaction span multiple HTTP requests (I know this has been discussed before multiple times, but I could not find whether the code below will work). During each request changes can be made to the domain objects. I want to implement it by storing a Hibernate session in the HTTP session for the duration of the transaction.

If I store a Hibernate session in a HTTP session I know I have to handle threading issues with simultaneous requests, and that it is possible that stale data is used. I will take care of both these issues.

Is the following sequence of code correct?

Request 1, thread 1
Code:
s = sf.openSession();
s.setFlushMode(FlushMode.COMMIT);

// Store 's' in the HTTP session
       
t = s.beginTransaction();
// Create/change domain objects/collections here
s.disconnect();

Request 2, thread 2
Code:
// Get 's' from the HTTP session

s.reconnect();
// Lazily load some collections
s.disconnect();

Request 3, thread 3
Code:
// Get 's' from the HTTP session

s.reconnect();
// Create/change domain objects/collections here
s.update(domainObject1);
s.update(domainObject2);
t.commit();
s.close();


I have found similar code in the forums, but there the transaction was committed before the session was disconnected. So it all comes down to: is it allowed to disconnect and reconnect a session without committing the transaction (provided the flush mode is set to commit)? I already build a test program and it works, but I want to make sure I won't encounter problems in the future.

Also, is it possible to set the default flush mode using properties? I could not find a property in the Environment class.

Thanks in advance for your repsonse.

_________________
Johan Stuyts


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 08, 2004 3:43 pm 
CGLIB Developer
CGLIB Developer

Joined: Thu Aug 28, 2003 1:44 pm
Posts: 1217
Location: Vilnius, Lithuania
Forget it.
if you will disconnect and close connection, transaction will be aborted.
if you will return connection to pool, you will break transactions.
If you will store connection in http session with uncomited transaction, web server will hung after a few "active" sessions.

Try to simulate transactions per multiple requests in memory, it is trivial if transaction result doe's not depend on data loaded not transactionaly, "optimistic locking" or some "clever certifier" can help too.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 09, 2004 4:18 am 
Newbie

Joined: Thu Jan 08, 2004 8:33 am
Posts: 9
Location: Abbekerk, the Netherlands
baliukas wrote:
Forget it.
if you will disconnect and close connection, transaction will be aborted.

The JDBC Transaction is aborted but the Hibernate transaction is not, right? Or does the Hibernate transaction track the connection state of the session? In my (simple) test it does not.

If only the JDBC transaction is aborted, this is OK. There should be nothing to abort because the flush mode is set to commit. No JDBC insert/update/delete statements should have been executed. Or am I wrong here?
baliukas wrote:
if you will return connection to pool, you will break transactions.
If you will store connection in http session with uncomited transaction, web server will hung after a few "active" sessions.

I want to store at most one Hibernate session per HTTP session. If an existing session is found at the start of a new use case, the transaction associated with that session is rolled back and that session is closed before a new session is created.

During a request the session will be open and connected. In between requests the session will be open and disconnected. The Hibernate transaction remains uncommitted until a request marked as committing, rolling back,or the start of a new use case is executed.
baliukas wrote:
Try to simulate transactions per multiple requests in memory, it is trivial if transaction result doe's not depend on data loaded not transactionaly, "optimistic locking" or some "clever certifier" can help too.

If I have to keep all changes of multiple requests in memory and commit them in the final request, I will have to synchronize another two representations: use case objects and database objects (currently synchronizing between HTTP data and domain model). In my opinion this is not trivial and will take too much time to build. Also, I will lose lazy loading which worked after a disconnect() and reconnect() in my test.

What do you mean with "clever certifier"? I could not find an explanation of the term.

_________________
Johan Stuyts


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 09, 2004 8:43 am 
CGLIB Developer
CGLIB Developer

Joined: Thu Aug 28, 2003 1:44 pm
Posts: 1217
Location: Vilnius, Lithuania
jstuyts wrote:

The JDBC Transaction is aborted but the Hibernate transaction is not, right? Or does the Hibernate transaction track the connection state of the session? In my (simple) test it does not.



Hibernate doe's not implement phisical transactions, hibernate transaction is a wrapper/abstraction for JDBC or JTA transactions.

jstuyts wrote:


If only the JDBC transaction is aborted, this is OK. There should be nothing to abort because the flush mode is set to commit. No JDBC insert/update/delete statements should have been executed. Or am I wrong here?


It depends on flush mode, if requests do not produce "write" to database then it is the same as I am talking about "simulate it in memory", "write" must not depend on "read" if you need it to work as realistic concurency control and http session as transaction. It is not very trivial to implement realistic custom concurency control this way and I think it can be "slow".

jstuyts wrote:
What do you mean with "clever certifier"? I could not find an explanation of the term.


"Certifier" is term used in concurency control teory, "optimistic locking" is more popular term, but it is not the same as hibernate "optimistic locking".
"clever" is my term :)

Optimistic concurency control strategy must work this way in theory:
All operations are executed as not conflictiong without locking(optimism) and conflict is detected before commit. If no conflicts detected then commit else abort.

"Concurency Control and Recovery" is a free book and you can download it from microsoft as pdf file and print it. If it is not a crime to citate books on public forums, I can paste a citation (my english is not good and I am not a good writer too).

The main goal of concurency control is to produce "serial" execution history, I am not sure about hibernate "optimistic locking", but I do not think it can detect all conflicts, it stores nothing in db or in some "global scope" about "read" and http session(transaction in your use case).


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 15, 2004 6:40 am 
Newbie

Joined: Thu Jan 08, 2004 8:33 am
Posts: 9
Location: Abbekerk, the Netherlands
Thanks for your help.

I did some more research and found out that Hibernate will flush the session if you perform a query on a table which is affected by updates. This means that updates before a disconnect() get lost.

Because of this I decided to implement it differently. I added transaction demarcations to the Struts actions. According to the requirements of an action the Hibernate session and the Hibernate transaction are created, committed, closed, etc.

I use at most one Hibernate session per HTTP session and I have implemented an HTTP session lock (which hopefully works) to make sure only one thread at a time accesses the Hibernate session. The JDBC specification says that JDBC classes should be thread safe, so requests for a single use case which are handled by multiple threads are no problem.

This scheme will make JDBC-connection pooling useless as JDBC connections are held on to for long periods of time. I also do not rollback the Hibernate transaction for actions which only need a short-lived, read-only connection. This means that currently there will be an open JDBC connection for each connected user. I plan to add the appropriate transaction demarcations to these actions which will rollback the Hibernate transaction.

Overall I think the system will work well when all is in place.

_________________
Johan Stuyts


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 16, 2004 5:05 am 
CGLIB Developer
CGLIB Developer

Joined: Thu Aug 28, 2003 1:44 pm
Posts: 1217
Location: Vilnius, Lithuania
It is very dangerous to store JDBC resources in memory. It is very trivial to reproduce some problems.
Example use case:
1. User enters password and login name,
2. application authenticates user and stores connection in http session.

Database has limit for connection count, maxAuthenticatedUsers <= maxConnections.

Example use case 2:

1. User enters password and login name,
2. application authenticates user and stores connection in http session.
3. User closed window (jsessionid cookie is lost and new session will be created)
4. GOTO 1

maxIterations <= maxConnections, single user can make your application to crash.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 16, 2004 5:37 am 
Newbie

Joined: Thu Jan 08, 2004 8:33 am
Posts: 9
Location: Abbekerk, the Netherlands
I know that a JDBC connection should be closed as soon as possible, but I don't see another way of implementing this. I need Hibernate sessions with lazy loading which span multiple HTTP requests. I don't have the time to do a complete rewrite of the application to include our own in-memory transactions framework.

I hope I won't encounter either the connection limit or the authenticated user limit of the database (PostgreSQL). I can decrease the number of open connections by rolling back as much read-only Hibernate sessions as possible at the end of an HTTP request.

Your second use case is a bigger problem. I could set a limit on the number of HTTP sessions originating from an IP address, but I will only build that when necessary. The HTTP session timeout of the application is currently set to 2 hours, so a number of Hibernate sessions will be open even though the user has closed his browser. Again, I hope to reduce this number by rolling back read-only Hibernate sessions at the end of an HTTP request.

Thanks again for your help.

_________________
Johan Stuyts


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 16, 2004 5:40 am 
Newbie

Joined: Thu Jan 08, 2004 8:33 am
Posts: 9
Location: Abbekerk, the Netherlands
There is one problem with my solution. If you reattach an object to a Hibernate session, any object it references does not automatically get reattached too. For example:


[list=]Open new session[/list]
[list=]Reattach person A[/list]
[list=]Get person A's partner (person B)[/list]
[list=]Iterate over person B's collection of children[/list]

This will result in [/list]

_________________
Johan Stuyts


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 16, 2004 5:43 am 
Newbie

Joined: Thu Jan 08, 2004 8:33 am
Posts: 9
Location: Abbekerk, the Netherlands
There is one problem with my solution. If you reattach an object to a Hibernate session, any object it references does not automatically get reattached too. For example:

Load person A and B in a session
Open new session
Reattach person A
Get person A's partner (person B)
Iterate over person B's collection of children

This will result in a collection not initialized exception. To solve this reattach person B to the session before iterating over it's collection of children. It's a nuisance, but I don't see how I could do this automatically:

Load person A and B in a session
Open new session
Reattach person A
Get person A's partner (person B)
Reattach person B
Iterate over person B's collection of children

_________________
Johan Stuyts


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 16, 2004 6:26 am 
CGLIB Developer
CGLIB Developer

Joined: Thu Aug 28, 2003 1:44 pm
Posts: 1217
Location: Vilnius, Lithuania
You can try this way, if you have no time to redisign application, but I am afraid it will not work.

It can problematic on postgresql to have a lot of connections, postgres creates process per connection and it will waste shared memory for IPC(It is better to keep connection count "small" and buffer count "large" to use shared memory for cache).


BTW if some transaction is known to be readonly,
try to use "SET TRANSACTION READ ONLY", I have never used it and I am not sure about postgresql implementation, but it can be the major preasumption for backend implementation to optimize concurency control.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 16, 2004 6:40 am 
Newbie

Joined: Thu Jan 08, 2004 8:33 am
Posts: 9
Location: Abbekerk, the Netherlands
Thanks for the tip about read-only connections. I might add that because it's not a big change and could provide performance benefits.

Like I said before I have to take my chances with having multiple connections open for a long time. Unless someone can provide me a simple, transparent alternative.

_________________
Johan Stuyts


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jan 18, 2004 5:06 am 
CGLIB Developer
CGLIB Developer

Joined: Thu Aug 28, 2003 1:44 pm
Posts: 1217
Location: Vilnius, Lithuania
jstuyts wrote:
Like I said before I have to take my chances with having multiple connections open for a long time. Unless someone can provide me a simple, transparent alternative.

I am thinking about this. It is possible to implement in theory, but I am not sure it will perform in practice. It can take a lot of time to tune and to experiment with this idea :

1. Asume we have single client for simplicity.
2. Set autocommit = on, we will use custom transactions.
3. generate transaction id in application, store this ID in http session.
4. add XID colum for all tables (Interceptor can manage this property for objects)
5. Store commit list, it can be bit set in local file or table with single column in DB, put XID to list on "logical commit".
6. transform all queries to use app XID.
7. implement concurency control, it can be very trivial for some use cases:
select rows where XID is in commit list or XID == current XID (READ_COMMITED), trow exception on conflict for update or delete( Aggressive CC ).
8. Implement recovery:
Delete all rows from DB where XID not in commit list (we delete rows with current XID on "logical abort" ).
9. Implement garbage collection, Use DB transactions to mark all rows as commited (set XID to some constant value) and truncate commit list.

System tables can be used to implement framework for "custom transactions". This is very aggressive way, but you can use any connection for operation without wasting resources, but custom concurency control algorythm can add more overhead itself.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jan 18, 2004 7:41 am 
CGLIB Developer
CGLIB Developer

Joined: Thu Aug 28, 2003 1:44 pm
Posts: 1217
Location: Vilnius, Lithuania
forget custom transactions, it is a nonsence too. It needs more things like LOCK table, or some data structure in memory (for single client) to detect
read-write conflict for update and delete. Application will be more expensive than data itself and it will be better to lose data or to change use case to use transaction per request.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 13 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.