Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migration of unmigrated content due to installation of a new plugin
indent
1
1
{color:green}Have any suggestion on how improve this wiki?  Please give us your feedback at [mailto:csf-support@mit.edu]{color}
Panel

Quick Links to:

Table of Contents
minLevel3
1

This

document

outlines

the

issues

involved

in

dealing

with

concurrent

database

updates

in

our

web

apps

-

the

situation

where

more

than

one

user

tries

to

update

the

same

piece

of

data

at

the

same

time.

See

"Chosen

Solution"

below

for...

our

chosen

solution.

Panel

About this Page

indent
1
1

To

set

the

scene,

here's

a

typical

situation

in

our

web

applications:

* Dave pulls up data in his browser. Goes to lunch. * Shahla pulls up same data, makes a change and saves. Changes are committed to the DB. * Dave comes back from lunch, changes the data on his screen and saves. Changes are committed, overwriting Shahla's changes. Obviously this isn't good; when Dave finally submits changes, we need the app to detect that someone else has changed the data, and to handle the situation appropriately. Two aspects to doing this are: # *Detection* \- how to detect a concurrent update # *Handling* \- what to do if we detect a concurrent update We'll take these in reverse order:

  • David pulls up data in his browser. Goes to lunch.
  • Felicia pulls up same data, makes a change and saves. Felicia's changes are committed to the DB.
  • David comes back from lunch, changes the data on his screen and saves. David's changes are committed, overwriting Felicia's changes.

Obviously this isn't good; when David finally submits changes, we need the app to detect that someone else has changed the data, and to handle the situation appropriately. Two aspects to doing this are:

  1. Detection - how to detect a concurrent update
  2. Handling - what to do if we detect a concurrent update

We'll take these in reverse order:

Panel

Example Situation

indent
1
1

There

are

three

approaches

to

handling

a

concurrent

update

situation:

# _

  1. Last
  1. commit
  1. wins
_
  1. .
  1. This
  1. is
  1. the
  1. situation
  1. described
  1. above,
  1. where
Dave
  1. David wipes
  1. out
Shahla
  1. Felicia's
  1. changes.
  1. This
  1. is
  1. what
  1. we're
  1. doing
  1. most
  1. of
  1. the
  1. time
  1. in
  1. our
  1. apps,
  1. and
  1. it
  1. does
  1. not
  1. involve
  1. any
  1. detection
  1. of
  1. a
  1. concurrent
  1. update.
# _
  1. First
  1. commit
  1. wins
_
  1. .
  1. In
  1. this
  1. scenario,
Dave
  1. David would
  1. come
  1. back
  1. from
  1. lunch,
  1. attempt
  1. to
  1. save
  1. his
  1. changes,
  1. and
  1. see
  1. an
  1. error
  1. message
  1. "Someone
  1. else
  1. has
  1. changed
  1. the
  1. data.
  1. Your
  1. changes
  1. have
  1. not
  1. been
  1. saved".
Dave
  1. David's
  1. changes
  1. would
  1. not
  1. be
  1. saved
  1. to
  1. the
  1. DB.
  1. This
  1. does
  1. require
  1. detection
  1. of
  1. a
  1. concurrent
  1. update.
# _
  1. Merge
_
  1. .
  1. Here,
Dave
  1. David would
  1. be
  1. presented
  1. with
  1. a
  1. message
  1. telling
  1. him
  1. that
  1. someone
  1. else
  1. had
  1. changed
  1. the
  1. data,
  1. and
  1. would
  1. be
  1. provided
  1. with
  1. a
  1. UI
  1. to
  1. give
them
  1. him a
  1. way
  1. of
  1. merging
  1. his
  1. changes
  1. with
Shahla
  1. Felicia's
  1. changes.
  1. This
  1. does
  1. require
  1. detection
  1. of
  1. a
  1. concurrent
  1. update.

Unless

there's

a

clear

business

requirement

for

the

merge

option,

I

think

we

need

to

take

the

second

approach,

"first

commit

wins".

In

order

to

pursue

this

approach,

we

do

need

to

have

a

mechanism

for

detecting

concurrent

updates.

Which

leads

to...

Panel

Handling a Concurrent Update Situation

indent
1
1

Hibernate

does

offer

a

facility

for

detecting

concurrent

updates

by

providing

a

version

attribute.

A

database

column

(usually

integer

or

timestamp)

can

be

designated

as

a

version

field

-

for

new

tables

we

typically

use

the

integer

type.

However,

the

way

most

of

our

web

apps

are

written,

this

offers

only

limited

protection.

Generally,

when

a

user

updates

data

in

a

web

app,

there

are

two

steps:

#

  1. User
  1. requests
  1. the
  1. data;
  1. we
  1. read
  1. the
  1. data
  1. from
  1. the
  1. database
  1. and
  1. present
  1. it
  1. on
  1. the
  1. screen.
#
  1. User
  1. changes
  1. data
  1. on
  1. the
  1. screen
  1. and
  1. submits.

There

can

be

a

significant

period

of

time

between

these

two

steps.

In

step

2

(posting

of

data

to

be

updated),

we

typically

do

this

in

the

same

HTTP

request:

#

  1. re-read
  1. the
  1. data
  1. from
  1. the
  1. database
#
  1. apply
  1. the
  1. changes
  1. from
  1. the
  1. web
  1. form
  1. to
  1. this
  1. data
#
  1. save
  1. the
  1. modified
  1. data
  1. &
  1. commit

Because

we

re-read

the

data,

the

version

number

check

offered

by

Hibernate

only

will

protect

us

from

concurrent

updates

taking

place

during

this

request.

As

the

request

is

typically

very

fast

(<1s),

this

is

unlikely

to

happen.

What

we

want

to

do

is

to

detect

any

changes

in

the

database

that

occurred

between

the

data-retrieval

step

(step

1)

and

the

data-posting

step

(step

2)

(i.e.

while

Dave

David is

at

lunch).

For

this

to

happen,

step

2

needs

to

know

what

the

version

field

was

in

step

1.

So

far

I've

read

about

three

techniques

for

doing

this:

#

  1. The
  1. web
  1. app
  1. stores
  1. the
  1. version
  1. number
  1. in
  1. the
  1. web
  1. page
  1. as
  1. a
  1. hidden
  1. field
  1. in
  1. step
  1. 1.
  1. The
  1. form
  1. submission
  1. in
  1. step
  1. 2
  1. then
  1. submits
  1. this
  1. version
  1. number
  1. along
  1. with
  1. the
  1. rest
  1. of
  1. the
  1. data,
  1. and
  1. then
  1. in
  1. 2b
  1. ("apply
  1. the
  1. changes
  1. from
  1. the
  1. web
  1. form
  1. to
  1. this
  1. data")
  1. the
  1. version
  1. number
  1. from
  1. the
  1. form
  1. is
  1. copied
  1. into
  1. the
  1. domain
  1. object.
  1. When
  1. the
  1. data
  1. is
  1. comitted,
  1. Hibernate
  1. should
  1. then
  1. pick
  1. up
  1. the
  1. version
  1. mismatch.
\\ \\ # At the end of step 1, the domain object is stored in the user's HttpSession - it becomes a "detached object" once the Hibernate Session is closed at the end of the request. Then step 2, instead of re-reading the data from the database, would get the object from the HttpSession and attach it to the newly created Hibernate Session. In this way, the object would contain the original version number, and Hibernate would catch version mismatch on commit. \\ \\ # At the end of step 1, the Hibernate Session is stored in the user's HttpSession. The Hibernate Session remains active for the entire "conversation" - this technique is described as


  1. At the end of step 1, the domain object is stored in the user's HttpSession - it becomes a "detached object" once the Hibernate Session is closed at the end of the request. Then step 2, instead of re-reading the data from the database, would get the object from the HttpSession and attach it to the newly created Hibernate Session. In this way, the object would contain the original version number, and Hibernate would catch version mismatch on commit.

  2. At the end of step 1, the Hibernate Session is stored in the user's HttpSession. The Hibernate Session remains active for the entire "conversation" - this technique is described as "session-per-conversation".
  1. Step
  1. 2
  1. would
  1. retrieve
  1. the
  1. Hibernate
  1. Session
  1. from
  1. the
  1. user's
  1. HttpSession
  1. and
  1. all
  1. domain
  1. objects
  1. associated
  1. with
  1. the
  1. Hibernate
  1. Session
  1. in
  1. step
  1. 1
  1. would
  1. be
  1. available
  1. to
  1. step
  1. 2.
  1. In
  1. this
  1. approach,
  1. it
  1. would
  1. be
  1. essential
  1. for
  1. the
  1. end
  1. of
  1. step
  1. 1
  1. to
  1. disconnect
  1. the
  1. JDBC
  1. connection;
  1. and
  1. for
  1. the
  1. beginning
  1. of
  1. step
  1. 2
  1. to
  1. establish
  1. a
  1. new
  1. JDBC
  1. connection.

Panel

Detection

indent
1
1

For

Education

Systems

web

apps

we

have

decided

to

use

detection

option

1

-

passing

the

version

number

to

the

JSP

in

the

GET

request,

and

moving

the

version

number

into

the

retrieved

Hibernate

entity

during

the

POST

request.

Hibernate

itself

will

then

take

care

of

checking

version

number

on

update.

If

a

concurrency

problem

is

encountered,

Spring

should

throw

a

org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException.

This

exception

should

be

intercepted

by

the

web

app

and

an

appropriate

UI

action

should

take

place

(for

example,

forwarding

to

an

error

page,

or

displaying

a

message

on

the

current

page).

There

is

a

simple

web

app

demonstrating

this

scheme.

It

uses

an

in-memory

H2

database

so

should

be

self-contained

:

Panel

Chosen Solution

indent
1
Code Block


svn+ssh://svn.mit.edu/csf-playground/web-concurrency

Also

there

is

a

non-web

project

that

uses

a

unit

test

\

[TestConcurrency.testConcurrentUpdate()

\

]

to

illustrate

the

concurrent

update

situation:

Code Block


svn+ssh://svn.mit.edu/csf-playground/hibernate-concurrency

NOTE:

if

the

web

page

displays

multiple

objects

that

can

be

updated,

the

version

numbers

of

all

the

objects

must

be

included

in

the

JSP,

and

must

all

be

applied

to

the

relevant

entity

objects

when

the

form

data

is

posted.

Panel

IMPORTANT: Load vs Get

There is a difference between Hibernate's load and get methods for retrieving data that could be important in checking for concurrent updates:

  • load does not issue a SQL SELECT statement to retrieve data. It simply creates a proxy object that follows the structure of the relevant domain class.
  • get issues an immediate SQL SELECT statement, creating a new domain class containing the data read from the database.

The reason this is important for concurrent update checking is that a proxy object generated by a load will not contain the version information we need.

It probably makes sense then to always use a hibernate get for retrieval.

This outline shows the issue - two sessions working with an object with the same identifier. Initial version for object in DB is 10.

Code Block

    Session 1                  Session 2

    ---------                  ---------

    Load object

    Wait a while..

                               Load object

                               Modify object property

                               [triggers db 'select' -

                                version read as 10]

                               Commit

                               [triggers db update,

                                version modified to 11]

    Modify object property

      [triggers db 'select' -

      version read as 11]

    Commit

      [triggers db update,

      version modified to 12]

We actually want session 1's commit to fail with an optimistic lock exception, but it will succeed here.

Using "get" instead of "load" works around the problem, because get will immediately issue a select, and the version numbers will be loaded at the correct times for the optimistic lock checking.

We actually want session 1's commit to fail with an optimistic lock exception, but it will succeed here.

Using "get" instead of "load" works around the problem, because get will immediately issue a select, and the version numbers will be loaded at the correct times for the optimistic lock checking.

Some useful information is available here: * [Hibernate (3.3) Documentation|http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/objectstate.html] * ["Hibernate in Action" PDF|https://web.mit.edu/sturner/www/secure/HibernateInAction.pdf] * ["Java Persistence with Hibernate" PDF|https://web.mit.edu/sturner/www/secure/JavaPersistenceWithHibernate.pdf]
Panel

Further Reading

Some useful information is available here:

Panel

Further Reading

indent
11