-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MDEV-34986: storage/innobase/dict/dict0dict: add a RAII class to freeze a dict_sys_t #3529
base: main
Are you sure you want to change the base?
Conversation
These macros shall be used if there are other arguments. Unlike the old variants, they start with a comma.
This replaces a lot of manual freeze() and unfreeze() call. Doing it with RAII is safer and easier. I did not replace all freeze()/unfreeze() pairs because some callers unfreeze and re-freeze in the middle of a scope. Sometimes, adding a new scope can be added just for such a RAII object. Refactoring that can be done later. Notes: - Instead of using the global variable `dict_sys`, I decided to pass a `dict_sys_t` reference parameter, because I believe it will be necessary to eliminate that global variable eventually (in order to have a per-catalog instance). Hard-coding this global variable here would generate identical (not better) machine code and would be a step in the wrong direction. - The new macros `SRW_LOCK_ARGS2` and `SRW_LOCK_CALL2` were necessary because this is the first time those debug-only parameters are forwarded from a function that has more parameters (i.e. the dict_sys_t reference).
1bd6f5a
to
afc52cf
Compare
CI failures due to a dumb typo; I force-pushed an amended version. |
Thank you for your contribution. It was great to hear your related talk in Berlin last Tuesday. It is a pity that we did not have a chance to talk after that. I did not have a chance to check the related iovisor/bcc#5108 yet. With @montywi’s current (not yet published) implementation of catalogs, there will continue to be a single global InnoDB infrastructure, which would include the singleton For catalogs or multi-tenancy to make any sense, I think that at the minimum, the buffer pool must be shared. I understood that implementations in other RDBMS may have per-tenant file structure, such as separate write-ahead, transaction metadata and data dictionary for each tenant. That kind of a solution would require pushing down a catalog or tenant ID to the lower level, such as the buffer pool and mini-transactions. It could incur a considerable overhead for the case that there is only a single tenant or catalog. It would also be more complex to implement than the what @montywi currently has shared with your organization. Long story short, I think that the singleton The bug that was featured in your talk would not have been prevented by this refactoring. The The fix b2654ba moves some logic that used to be duplicated into a non-inline function I think that there can be some benefit of having RAII around synchronization primitives. We are using it in b08448d and a variant of it in |
Of course not! This is just a source-code level change with no runtime effect. It helps keeping the code readable; and of course RAII is safer, less prone to human mistakes. While MariaDB is formally written in C++, it could go much further to take advantage of C++'s features to make it safer. When I read the code to try to understand the |
The generated code is mostly identical. In a perfect world, all of this would collapse to exactly identical machine code; but there are compiler imperfections. For example, GCC is a bit clumsy about the trivial method Other functions may grow larger because RAII gives you stronger guarantees; i.e. the generated code is a bit longer, but actually better than before. RAII guarantees that the destructor will be called even if an exception was thrown. That requires a tiny bit of extra code for stack unwinding. People have different opinions about C++ exceptions. For my own code, I'm more than willing to pay the tiny price for this stack unwinding code (with overhead that is hardly even measurable). But fact about MariaDB is that C++ exceptions are not disabled, therefore you are already paying that price. Exceptions are indeed used in very few places, but 99% of the code misses My point is: the code is not 100% identical, but it is either
|
With this PR, the executable grows by 572 bytes. But if I apply #3531 first, then this PR adds only 256 bytes, because the compiler can now omit some of the unnecessary stack unwinding safety. |
Would the executable size grow less if you did not add any data member to your RAII guard, but instead made it refer directly to the singleton |
In all cases I analyzed in the disassembly, this reference was optimized away by GCC, because GCC knew it was guaranteed to always point to the global variable. There could be optimization pitfalls in other/similar cases, though: if this was not a global object, but a global pointer to an object, GCC would not be disallowed to optimize it away because the pointer value could change meanwhile. But in that case, the generated code would still be better because GCC would (likely) cache the pointer in a register instead of having to access RAM again to reload the pointer. Edit: I just verified - the binary is exactly identical whether I use the global |
Thank you. With By the way, while the The purpose of MDL or the old use of "global MDL" as in |
I added a JIRA ticket. Do you want me to rebase this PR on a different branch? |
FWIW... Apparently back a while ago I did write Marko:
I got request to elaborate this quote here. There certainly are good things that RAII locking has to offer:
So here are problems that did make me come to this conclusion back then...
All in all (IMHO) it gives more headache than good. The only use case for RAII locking that I'd grudgingly agree with must meet the following requirements:
If you ask me, I'd put effort in getting rid of
should either move Another example:
This should become member of Good hunting! |
Hi @svoj , just a few thoughts to share:
It saves the programmer from explicitly writing code to unlock a primitive, yet the primitive is still unlocked as intended by the programmer.
This isn't the most important part of RAII, what really matters is consistency of lock-unlock pairs are preserved as part of the system design inherently.
I'm not sure what you meant here, can you please clarify?
Unfortunately, all programming constructs may be abused, nothing is foolproof. I'm not sure that this is a counterargument to using RAII. Code blocks are an intentional way of indicating the critical section and IIRC Facebook's
Both of these points are more of a tooling question as LSP + vim or LSP/eglot + emacs (and other tools) can easily find such references.
This sounds like a correctness problem in the original code; shouldn't destructors be called when objects go out of scope?
These are reasonable to me, with flexibility on (5) as you allowed already. |
I would also like to point out that I am working on reducing contention on One point that @svoj already made is that a RAII helper easily leads to releasing latches in LIFO order. Sometimes we try to release some easily contended latches in FIFO order in order to minimize the size of a critical section for a popular latch, such as |
...skip...
What I meant was:
Here
Probably they can find such references and probably they will be more bullet-proof compared to grep. OTOH it can happen that in real world simplicity beats advanced constructs.
For contested locking primitives we may want to make critical section as small as possible. If destructors don't strictly need to be executed under protection, they should be moved out of critical section. In this case we have no control which destructor is called first. This is very similar to what @dr-m explained above. Another question is if we can make use of |
Oh yes, this makes sense, thank you for clarifying.
Understood, yes in general as little as necessary should be done in the critical section.
I would prefer this, I'm not sure how far back in terms of |
Meanwhile, #3729 was merged and #3889 should soon remove some code duplication around Exclusive We can keep this ticket open as a reminder to revisit the topic, once we no longer hold |
@dr-m if pull request is not intended to be accepted it should be closed. MDEV should be the reminder to revisit the topic. |
https://jira.mariadb.org/browse/MDEV-34986
Description
This replaces a lot of manual freeze() and unfreeze() call. Doing it with RAII is safer and easier.
I did not replace all freeze()/unfreeze() pairs because some callers unfreeze and re-freeze in the middle of a scope. Sometimes, adding a new scope can be added just for such a RAII object. Refactoring that can be done later.
Notes:
Instead of using the global variable
dict_sys
, I decided to pass adict_sys_t
reference parameter, because I believe it will benecessary to eliminate that global variable eventually (in order to
have a per-catalog instance). Hard-coding this global variable here
would generate identical (not better) machine code and would be a
step in the wrong direction.
The new macros
SRW_LOCK_ARGS2
andSRW_LOCK_CALL2
were necessarybecause this is the first time those debug-only parameters are
forwarded from a function that has more parameters (i.e. the
dict_sys_t reference).
Release Notes
Nothing. Internal code change only with no runtime effect.
How can this PR be tested?
No runtime effect.
Basing the PR against the correct MariaDB version
main
branch.PR quality check