yiiext / nested-set-behavior

AR models behavior that allows to work with nested sets tree.
http://www.yiiframework.com/extension/nestedsetbehavior/
BSD 3-Clause "New" or "Revised" License
157 stars 64 forks source link

Concurrency Problems #31

Open caleblloyd opened 11 years ago

caleblloyd commented 11 years ago

The current implementation of this behavior only provides concurrency in a single Yii Application instance scenario. The static variable $_cached tracks all of the instances of Nested Set AR's in a single Yii Application instance and keeps left, right, level, and root attributes current.

The problem with this assumption is that each request served by a web server generates a new instance of a Yii Application. Furthermore, load balanced applications can create even more instances of Yii Applications on different servers. The static $_cache variable does not track anything outside the current Yii Application instance, meaning that the possibility for a stale cache exists both on a single and multi server implementation.

Databases transactions can solve this problem. This behavior does use transactions, but it uses potentially stale data that was fetched before the transaction was started. The following methods use potentially stale data:

-save(): This will by default save left, right, root, and level attributes. These could have been changed on another instance since the model was loaded. These attributes should not be saved by default.

-delete(): This uses $owner attributes, which were loaded before the DB Transaction was started. These attributes should be refreshed once the DB Transaction starts.

-addNode(): This uses $key (computed based off $target) and $target attributes, which were loaded before the DB Transaction was started. $target should be refreshed once the DB Transaction starts and $key should be computed after $target has been refreshed.

-moveNode(): same problem as addNode() except $owner attributes are also used.

-moveAsRoot(): same problem as delete().

Please let me know if you think refreshing this data inside of the Database transaction would fix the Concurrency issues that presently exist. I'm happy to contribute a fix.

caleblloyd commented 11 years ago

I've put together a solution using SELECT...FOR UPDATE at the beginning of each transaction that modifies a tree. My only concern here is that I don't think SELECT...FOR UPDATE is database agnostic and therefore would need to be a configuration option that could be turned off for databases that don't support it.

I'm currently working off of this fork. It needs further testing, and I'd also like input on what needs to change for this to get Pulled into Master: https://github.com/caleblloyd/nested-set-behavior/commit/4ea2b24a58a20e5b9754180c0eb1205ad13862ed

creocoder commented 10 years ago

@caleblloyd Can you provide any real-life task when we need support concurrency in several Yii Applications?

caleblloyd commented 10 years ago

One example would be an Application that allows Users to signup for accounts and stores each User ID in a Nested Set Hierarchy as they signup. In a high-traffic application, two users could easily sign up at the same time resulting in a corrupted Nested Set Tree.