Indirectly, the parent node *does* include state information like info about number of nodes. Not directly, i.e. in the node's attribute map. But indirectly, via the getChildren() and getChildrenNames() methods. If your application uses those methods and counts on transactional consistency in the results, that's when you'd set lockParentForChildInsertRemove to true.
Personally I've never written an app where I needed to set lockParentFroChildInsertRemove to true.
Thanks Brian. I think I kind of get the implication. However, arent getChildren or getChildrenNames methods considered read operations, and if so, with mvcc, the reads wouldnt need a write lock anyway. The read calls would return something that is based on isolation level (REPEATABLE_READ) in that case. So how would that affect the application.
By the way, the only use case for which we need getChildren is to retrieve all objects of a certain type that are cached. In that case, if we get a snapshot from before any of the current running transaction started, that is good enough. The more I think about it, I am pretty sure we don't need this in our application. But I am just trying to understand if there is something obvious that I am ignoring, like nodes getting lost or overwritten by different threads etc. For eg. just because I didnt set this option to true, if two threads insert two nodes into this parent at the same time, we would never run into a situation where one of the node is lost or overwritten by the other or something of that nature, right? I wouldn't think so, but just confirming. Now, its totally ok if the read calls at around the same time don't include this/these node because of the transaction being not committed yet.
Thanks for the question; I didn't personally work on the MVCC code and the discussions I'd been on didn't specifically discuss handling of children. So, had to chat with Jason Greene and look into the code, both of which are always good. :-)
As you said getChildren(Names) is a read operation, so with MVCC it won't block due to a concurrent write.
If you need to avoid phantom reads with getChildren(Names), i.e. do two reads in the same tx and not have children appear/disappear, you need to use lockParentForChildInsertRemove. REPEATABLE_READ is implemented by making a copy of the node before writing and storing it in a context object associated with the transaction, and then only inserting that copy into the main tree on tx commit. But the addition/removal of a child is only considered to be a "write" if lockParentForChildInsertRemove=true. If lockParentForChildInsertRemove=false, no copy of the parent is made, so the inserts/removals become visible to other threads as soon as they commit.
This behavior is consistent with the definition of REPEATABLE_READ in the java.sql.Connection javadocs, which says phantom reads can occur with R_R. Database notions never map exactly to a tree structure, but our interpretations have always treated new child nodes as being analogous to new db records returned by a query.
If you do set lockParentForChildInsertRemove=true, concurrent insertions/removals won't occur, but only one will proceed at a time; any others will block until the first completes.