Knut Anders Hatlen wrote:
Thanks Bryan, that could be the case. I have done some more
investigation and it seems like the method is only called when a
B-tree container is opened with a lock policy. It also seems like the
B-tree containers always are opened with a null/NoLocking lock policy
(regardless of isolation level) and therefore the method is never
called.
Derby uses data only locking, so it doesn't get locks on rows in the
btree. Instead it gets locks on the rows that the rows in the btree
point at.
However, I made an interesting discovery in
B2IRowLocking3.lockRowOnPage(). It uses the following procedure for
locking a row while holding a latch:
1) Try to lock the row with timeout == NOWAIT.
2) If the lock couldn't be obtained without waiting, manually
release the latch and try to lock the row with a normal timeout.
I don't see why this couldn't be done similarly in the few cases that
maybe some time in the future will need that functionality. Perhaps we
could change those calls and reduce some of the complexity of the lock
manager? I generally prefer to optimize for the most common and/or
least complex cases, and let the more complex cases pay the cost of
extra complexity themselves. If the more complex cases are only for
possible future use, I think that point is even more valid.
Also, I'm not sure that using the lock manager for latching is optimal
in the first place. The description of Derby's B-tree implementation
[1], says that the implementation is similar to ARIES/IM [2].
According to the ARIES/IM report,
"Acquiring a latch is cheaper than acquiring a lock (in the
no-conflict case, 10s of instructions versus 100s of instructions),
because the latch control information is always in virtual memory in
a fixed place, and direct addressability to the latch information is
possible given the latch name. Since each transaction holds at most
2 or 3 latches simultaneously (...), the latch request blocks can be
permanently allocated to each transaction and initialized with
transaction ID, etc. right at the start of that transaction. On the
other hand, storage for individual locks may have to be acquired,
formatted, and released dynamically, and more instructions need to
be executed to acquire and release locks."
The decision to uses the lockmanager in derby was definitely not a
performance decision. It was a decsion to reuse existing code, and
leave the optimization to later. Along the way quite a few
optimizations were added to improve the latch path through the
lockmanger. I have to also say that from the supportability/ease of
development it has been
nice to have latches and locks in the same space so that latch/lock
deadlocks/waits (which are usually bugs) are automatically caught and
diagnosed using existing lock manager tools/error reporting. Another
factor was that we wanted to continue to have 100% pure java
implementation so we didn't have the access to the standard test/set
instructions other implementation might have. But I am sure one
could get pretty close.
Having said that it would be interesting if someone had time to
implement a higher performance latch implementation and plug it in
and see how much it helps. It would decrease the total time spent
in lock manager.
In Derby, latching is cheaper than locking, but not nearly as cheap as
the ARIES/IM report suggests. I have done some measurements and come
to the conclusion that setting one latch is about half the cost of
setting one lock. The reason why latching is so expensive in Derby, is
that it basically uses the same code as the locking, and therefore
gets some of the extra overhead caused by the locks' dynamic nature. I
think it would be a great improvement if latching of pages didn't have
to go through the lock table and instead just set a flag on the page
object.
Footnotes:
[1] http://db.apache.org/derby/papers/btree_package.html
[2] http://www.almaden.ibm.com/u/mohan/RJ6846.pdf