Issue Details (XML | Word | Printable)

Key: JCR-314
Type: Improvement Improvement
Status: Closed Closed
Resolution: Fixed
Priority: Major Major
Assignee: Unassigned
Reporter: Marcel Reutegger
Votes: 4
Watchers: 5
Operations

If you were logged in you would be able to see more operations.
Jackrabbit Content Repository

Fine grained locking in SharedItemStateManager

Created: 03/Feb/06 07:59 PM   Updated: 15/Jan/08 11:26 PM
Return to search
Component/s: jackrabbit-core
Affects Version/s: 0.9, 1.0, 1.0.1, 1.1, 1.1.1, 1.2.1, 1.2.2, 1.2.3
Fix Version/s: 1.4

Time Tracking:
Not Specified

File Attachments:
  Size
Text File Licensed for inclusion in ASF works ConfigurableISMLocking.patch 2007-12-17 11:21 AM Marcel Reutegger 33 kB
Text File Licensed for inclusion in ASF works fine-grained-locking-1.4.patch 2007-12-11 02:28 PM Esteban Franqueiro 18 kB
Text File Licensed for inclusion in ASF works FineGrainedISMLocking-v2.patch 2007-05-23 11:02 AM Marcel Reutegger 13 kB
Text File Licensed for inclusion in ASF works FineGrainedISMLocking.patch 2007-05-03 12:59 PM Marcel Reutegger 7 kB
Text File Licensed for inclusion in ASF works ISMLocking.patch 2007-05-03 12:59 PM Marcel Reutegger 18 kB
Image Attachments:

1. writes-per-second.jpg
(62 kB)
Issue Links:
Dependants
 

Resolution Date: 18/Dec/07 10:42 AM


 Description  « Hide
The SharedItemStateManager (SISM) currently uses a simple read-write lock to ensure data consistency. Store operations to the PersistenceManager (PM) are effectively serialized.

We should think about more sophisticated locking to allow concurrent writes on the PM.

One possible approach:

If a transaction is currently storing data in a PM a second transaction may check if the set of changes does not intersect with the first transaction. If that is the case it can safely store its data in the PM.

This fine grained locking must also be respected when reading from the SISM. A read request for an item that is currently being stored must be blocked until the store is finished.

 All   Comments   Work Log   Change History   Subversion Commits      Sort Order: Ascending order - Click to sort in descending order
Michael Neale added a comment - 11/Mar/07 05:51 AM
anyone for escalating this above a "minor" - its kind of a showstopper for some people ?

Stefan Guggisberg added a comment - 11/Mar/07 10:17 AM
i agree with michael neale's judgement
(http://www.mail-archive.com/users@jackrabbit.apache.org/msg02503.html),
changing priority to major.

Oliver Zeigermann added a comment - 14/Apr/07 10:02 PM
Any idea how this could be achieved?

If you use blocking locks, it is very hard to avoid deadlocks. Of course you can use an ordered sequence of locks for all requests to avoid deadlocks, but that would mean you need a list of all resources to be locked before you even start doing anything.

After my experience with the Slide server I can only recommend non blocking locks. Such locks would make a request fail if it is not able to acquire all locks necessay instead of letting it wait. This would require a notion of a transaction, however.

Marcel Reutegger added a comment - 03/May/07 12:59 PM
To drive this issue forward I created a patch that replaces the current rather hard coded use of a ReadWriteLock in SharedItemStateManager with an abstraction layer that allows alternative implementations.

The way locking currently works is implemented in DefaultISMLocking and an initial implementation of a more fine grained locking is available in FineGrainedISMLocking.

FineGrainedISMLocking does not allow concurrent writes but at least allows reads while a long running write takes place and the read does not conflict with the write. I think this is the most common use case (save a big file but still allow other sessions to read, which is currently not possible).

All tests pass with both the DefaultISMLocking and the FineGrainedISMLocking.

The first patch only includes the structural changes moving away from the hard coded ReadWriteLock in the SharedItemStateManager to the ISMLocking interface. It does not change the locking semantics that are currently in place. The second patch is the proposal for a more fine grained locking strategy.

Comments are welcome.

Oliver Zeigermann added a comment - 03/May/07 01:50 PM
As far as I can tell the FineGrainedISMLocking implementation is prone to deadlocks. At least when a DB is used as storage. Such a deadlock can occur distributed between DB and the Jackrabbit locking. The DB can not resolve the deadlock, as internally it looks like there is none. A transaction timeout in the DB is the only way to resolve such a situation.

In any case there should be a timeout for Jackrabbit locks. For one to to free locks that have never been freed, because some thread forgot to free them. Additionally, to make sure deadlocks between Jackrabbit and DB can be resolved quickly.

Marcel Reutegger added a comment - 04/May/07 08:01 AM
Oliver, thank you for your comment. However I do not see how a deadlock may occur between jackrabbit and the DB used in a persistence manager. Deadlocks always require that locks are acquired in a circular way. That's never the case between Jackrabbit and the DB. A call that returns from the DB will always have released all DB locks.

I'd appreciate if you were a bit more specific, e.g. provide a wait-for graph that shows a deadlock situation.

> In any case there should be a timeout for Jackrabbit locks. For one to to free locks that have
> never been freed, because some thread forgot to free them.

I disagree. This would clearly be a bug in Jackrabbit and should rather be fixed than worked around with timeouts.

Jukka Zitting added a comment - 14/May/07 06:01 PM
I briefly reviewed the patches, good work! I like how you've applied the strategy pattern.

My main concern is that this still doesn't address the concurrency limitations in the current persistence managers. Should that be handled as a separate issue? A more general issue is that this whole ISM core keeps getting more complex, and adding 1+2 new interfaces and all the implementing classes doesn't really help things. I'm not sure what to do about that...

Also, it would be nice if we had at least a few parallel test cases for excercising such code.

Marcel Reutegger added a comment - 15/May/07 07:57 AM
Jukka, thanks for your comments.

> My main concern is that this still doesn't address the concurrency limitations in the current
> persistence managers.

You are right, the proposed changes will not solve the concurrency limitations completely, but at least they should improve the current situation where no reads are possible through the SISM when a write is taking place. I think truely fine grained locking will not be possible with the current design and thus would require major re-structuring in the jackrabbit core. Something I'd rather like to postpone for a 2.0.

> [...] limitations in the current persistence managers. Should that be handled as a separate issue?

There are a number of other issues that are related. Even if persistence managers are able to perform concurrent writes, the index will have to support that too otherwise concurrent writes in the PM will be of limited use. And then there's the question how to process (synchronous) events for concurrent writes?

> A more general issue is that this whole ISM core keeps getting more complex, and adding 1+2
> new interfaces and all the implementing classes doesn't really help things.

I actually didn't want to make it more complex but rather easier to understand. I think the interfaces are of some value because they describe the locking contract independant of the implementation. They also allow to easily try out a different locking implementation / strategy.

> I'm not sure what to do about that...

I'm also not 100% happy with the proposed changes, but at least it solves the most annoying problem with the SISM, that it locks up completely during a large write.

I see the following options:
- We don't change anything right now and do it better the next time (NGP, jackrabbit 2.0, ...)
- Redesign the core now to better handle concurrency
- Accept the patch ISMLocking.patch and discuss a locking implementation/strategy that's better suited than the current one.

I'm obviously in favour of the last option ;) because it gives us the most bang for the buck and is by far less risky than option two.

> Also, it would be nice if we had at least a few parallel test cases for excercising such code.

I agree. I already created some test cases that perform concurrent versioning operations and basic content modifications. So if anyone is interested, feel free to write more of them. The class AbstractConcurrencyTest and its sub classes are a good starting point.

Jukka Zitting added a comment - 15/May/07 08:29 AM
> I'm obviously in favour of the last option ;) because it gives us the most bang for the buck and is by far less risky than option two.

Agreed.

Note that without solving the concurrency issues in persistence managers (both db and bundle) we're still stuck with fully serialized backend access. But this is one step in the correct direction; the second step would be to start removing the synchronization on the persistence managers.

Stefan Guggisberg added a comment - 16/May/07 02:41 PM
> - Accept the patch ISMLocking.patch and discuss a locking implementation/strategy that's better suited than the current one.

+1

btw, i did a couple of test runs using ConcurrentReadWriteTest against
 a) current trunk
 b) a) with ISMLocking.patch applied (using DefaultISMLocking)
 c) b) with FineGrainedISMLocking.patch applied (using FineGrainedISMLocking)

environment: macbook pro (intel, 2ghz), os-x 10.4.9, jre 1.4.2

results:

a)
**
   ~2'300 #writes
   ~2'300'000 #reads

b)
**
   ~2'300 #writes
   ~2'300'000 #reads

c)
**
   ~1'700 #writes
   ~2'300'000 #reads

=> c) shows a significant decrease in #writes while #reads doesn't seem
to be affected. i guess that FineGrainedISMLocking still has room for
improvement ;)



Marcel Reutegger added a comment - 23/May/07 11:02 AM
Here's a new version of FineGrainedISMLocking.

The performance should be better compared to the last version.

I'm not sure the test case is very representative though. Lot of monitor contention happens between the write thread and readers when item states are notified and the cache maps need to be accessed. I think finer grained locking makes this situation even worse because it allows more concurrency on the caches.

Marcel Reutegger added a comment - 23/May/07 01:22 PM
Applied the patches ISMLocking.patch and FineGrainedISMLocking-v2.patch with one slight change: the SISM used the default implementation for now.

Committed in revision: 540944

There are some remaining issues before we can use FineGrainedISMLocking:

- using FineGrainedISMLocking allows for concurrent access to item state caches, which were not possible before. We must ensure that all item state caches are prepared for that.
- do extensive testing
- we should check again if the performance is ok

Marcel Reutegger added a comment - 23/May/07 01:25 PM
The chart shows the writes per second of the writer thread using the
ConcurrentReadWriteTest for DefaultISMLocking and FineGrainedISMLocking.
The duration of the test is 120 seconds.

Jukka Zitting added a comment - 20/Jun/07 09:18 PM
It seems that saving a single large binary property still (revision 549230) completely blocks read access to other nodes. See my last comment on JCR-926 for more details.

Jukka Zitting added a comment - 21/Jun/07 08:52 AM
Forget my last comment, the svn trunk still has DefaultISMLocking as the default.

Pablo Rios added a comment - 05/Sep/07 03:46 PM - edited
It seems that during the time a versioning operation is in progress (writer) it is not possible to establish a new session to the repository. This seems to be so because access to the root node of the version histories (/jcr:system/jcr:versionStorage) is required to create a session and this node is included on the active writer changelog. In other words the reader need to access an item (root node of the version storage) that has a dependency on the active writer changelog.

From XAVersionManager constructor

        try {
            state = (NodeState) stateMgr.getItemState(vMgr.getHistoryRootId());
        } catch (ItemStateException e) {
            ...
        }

(An XAVersionManager is instantiated during the instantiation of an XASessionImpl)

Note: I've observed this in an XA environment.

Am I wrong or right ?

What may be the consequences of allowing a reader to get a read lock in this situation even if the writer's changelog includes the VERSION_STORAGE_NODE_ID ?

Marcel Reutegger added a comment - 14/Sep/07 08:57 AM
> What may be the consequences of allowing a reader to get a read lock in this situation even if the writer's changelog includes
> the VERSION_STORAGE_NODE_ID ?

That's probably a bad idea. It may happen that a reader sees inconsistent data.

But anyway, I think a versioning operation will modify only the version storage root node in rare cases. Merely when the version storage is nearly empty. As soon as there are enough versions created the version storage root node has all possible child nodes (named 00, 01, ... ff) and is not modified anymore.

Thomas Mueller added a comment - 14/Sep/07 09:18 AM
From http://www.mail-archive.com/users@jackrabbit.apache.org/msg02503.html
> for people wanting to store large blobs (something that people would look at using JCR for) - this is a showstopper.

If reading/writing large objects blocks others, it is showstopper.
But Data Store should solve this: http://issues.apache.org/jira/browse/JCR-926
Is it still a problem?

Are there other concurrency problems?

Esteban Franqueiro added a comment - 11/Dec/07 02:28 PM
This patch adds a lockingStrategy attribute to the Workspace and Versioning element tags. It's possible values are default, fine, and fine+ (this one was a POC we did here).

Marcel Reutegger added a comment - 17/Dec/07 11:21 AM
Esteban, thank you for your patch. I've modified it slightly and used a separate configuration element with a class attribute. I didn't include your changes to the FineGrainedISMLocking because I think they may lead to inconsistencies. As described easier in this issue, once the version storage in sufficiently populated, the login will not block anymore when a version operation is in progress.

I have also added a new version of the DTD (1.4) and updated all repository.xml files I found in the sources. Once the changes are committed I will add the DTD to the website.

Please note that this change is backward compatible. The ISMLocking element is optional and defaults to DefaultISMLocking (the currently hard coded implementation).

Marcel Reutegger added a comment - 18/Dec/07 10:42 AM
Committed ConfigurableISMLocking.patch in revision: 605173

Please note that there are still some open synchronization issues when using FineGrainedISMLocking!