diff options
author | Konstantin Osipov <kostja@sun.com> | 2009-12-04 02:29:40 +0300 |
---|---|---|
committer | Konstantin Osipov <kostja@sun.com> | 2009-12-04 02:29:40 +0300 |
commit | 911c673edf45616137d71f43f472be410ecb7511 (patch) | |
tree | b92c9a920e36266d14745ea0ed62dc5e44025dd3 /sql/mdl.h | |
parent | 8582b4de5d2a44f64de2f6aaa15638d288953939 (diff) | |
download | mariadb-git-911c673edf45616137d71f43f472be410ecb7511.tar.gz |
Backport of:
------------------------------------------------------------
revno: 2617.23.18
committer: Davi Arnaut <Davi.Arnaut@Sun.COM>
branch nick: 4284-6.0
timestamp: Mon 2009-03-02 18:18:26 -0300
message:
Bug#989: If DROP TABLE while there's an active transaction, wrong binlog order
WL#4284: Transactional DDL locking
This is a prerequisite patch:
These changes are intended to split lock requests from granted
locks and to allow the memory and lifetime of granted locks to
be managed within the MDL subsystem. Furthermore, tickets can
now be shared and therefore are used to satisfy multiple lock
requests, but only shared locks can be recursive.
The problem is that the MDL subsystem morphs lock requests into
granted locks locks but does not manage the memory and lifetime
of lock requests, and hence, does not manage the memory of
granted locks either. This can be problematic because it puts the
burden of tracking references on the users of the subsystem and
it can't be easily done in transactional contexts where the locks
have to be kept around for the duration of a transaction.
Another issue is that recursive locks (when the context trying to
acquire a lock already holds a lock on the same object) requires
that each time the lock is granted, a unique lock request/granted
lock structure structure must be kept around until the lock is
released. This can lead to memory leaks in transactional contexts
as locks taken during the transaction should only be released at
the end of the transaction. This also leads to unnecessary wake
ups (broadcasts) in the MDL subsystem if the context still holds
a equivalent of the lock being released.
These issues are exacerbated due to the fact that WL#4284 low-level
design says that the implementation should "2) Store metadata locks
in transaction memory root, rather than statement memory root" but
this is not possible because a memory root, as implemented in mysys,
requires all objects allocated from it to be freed all at once.
This patch combines review input and significant code contributions
from Konstantin Osipov (kostja) and Dmitri Lenev (dlenev).
Diffstat (limited to 'sql/mdl.h')
-rw-r--r-- | sql/mdl.h | 282 |
1 files changed, 172 insertions, 110 deletions
diff --git a/sql/mdl.h b/sql/mdl.h index b4b84a9ab24..103ab8130ba 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -23,7 +23,8 @@ class THD; -struct MDL_LOCK_DATA; +struct MDL_LOCK_REQUEST; +struct MDL_LOCK_TICKET; struct MDL_LOCK; struct MDL_CONTEXT; @@ -43,87 +44,147 @@ enum enum_mdl_type {MDL_SHARED=0, MDL_SHARED_HIGH_PRIO, MDL_SHARED_UPGRADABLE, MDL_EXCLUSIVE}; -/** States which metadata lock request can have. */ +/** States which a metadata lock ticket can have. */ -enum enum_mdl_state {MDL_INITIALIZED=0, MDL_PENDING, - MDL_ACQUIRED, MDL_PENDING_UPGRADE}; +enum enum_mdl_state { MDL_PENDING, MDL_ACQUIRED }; + + +/** Maximal length of key for metadata locking subsystem. */ +#define MAX_MDLKEY_LENGTH (1 + NAME_LEN + 1 + NAME_LEN + 1) /** - A pending lock request or a granted metadata lock. A lock is requested - or granted based on a fully qualified name and type. E.g. for a table - the key consists of <0 (=table)>+<database name>+<table name>. - Later in this document this triple will be referred to simply as - "key" or "name". + Metadata lock object key. + + A lock is requested or granted based on a fully qualified name and type. + E.g. They key for a table consists of <0 (=table)>+<database>+<table name>. + Elsewhere in the comments this triple will be referred to simply as "key" + or "name". */ -struct MDL_LOCK_DATA +class MDL_KEY { - char *key; - uint key_length; - enum enum_mdl_type type; - enum enum_mdl_state state; +public: + const uchar *ptr() const { return (uchar*) m_ptr; } + uint length() const { return m_length; } + + const char *db_name() const { return m_ptr + 1; } + uint db_name_length() const { return m_db_name_length; } + + const char *table_name() const { return m_ptr + m_db_name_length + 2; } + uint table_name_length() const { return m_length - m_db_name_length - 3; } -private: - /** - Pointers for participating in the list of lock requests for this context. - */ - MDL_LOCK_DATA *next_context; - MDL_LOCK_DATA **prev_context; /** - Pointers for participating in the list of satisfied/pending requests - for the lock. - */ - MDL_LOCK_DATA *next_lock; - MDL_LOCK_DATA **prev_lock; + Construct a metadata lock key from a triplet (type, database and name). - friend struct MDL_LOCK_DATA_context; - friend struct MDL_LOCK_DATA_lock; + @remark The key for a table is <0 (=table)>+<database name>+<table name> -public: - /* - Pointer to the lock object for this lock request. Valid only if this lock - request is satisified or is present in the list of pending lock requests - for particular lock. + @param type Id of type of object to be locked + @param db Name of database to which the object belongs + @param name Name of of the object + @param key Where to store the the MDL key. */ - MDL_LOCK *lock; - MDL_CONTEXT *ctx; + void mdl_key_init(char type, const char *db, const char *name) + { + m_ptr[0]= type; + m_db_name_length= (uint) (strmov(m_ptr + 1, db) - m_ptr - 1); + m_length= (uint) (strmov(m_ptr + m_db_name_length + 2, name) - m_ptr + 1); + } + void mdl_key_init(const MDL_KEY *rhs) + { + memcpy(m_ptr, rhs->m_ptr, rhs->m_length); + m_length= rhs->m_length; + m_db_name_length= rhs->m_db_name_length; + } + bool is_equal(const MDL_KEY *rhs) const + { + return (m_length == rhs->m_length && + memcmp(m_ptr, rhs->m_ptr, m_length) == 0); + } +private: + char m_ptr[MAX_MDLKEY_LENGTH]; + uint m_length; + uint m_db_name_length; }; + /** - Helper class which specifies which members of MDL_LOCK_DATA are used for - participation in the list lock requests belonging to one context. + Hook class which via its methods specifies which members + of T should be used for participating in MDL lists. */ -struct MDL_LOCK_DATA_context +template <typename T, T* T::*next, T** T::*prev> +struct I_P_List_adapter { - static inline MDL_LOCK_DATA **next_ptr(MDL_LOCK_DATA *l) - { - return &l->next_context; - } - static inline MDL_LOCK_DATA ***prev_ptr(MDL_LOCK_DATA *l) - { - return &l->prev_context; - } + static inline T **next_ptr(T *el) { return &(el->*next); } + + static inline T ***prev_ptr(T *el) { return &(el->*prev); } }; /** - Helper class which specifies which members of MDL_LOCK_DATA are used for - participation in the list of satisfied/pending requests for the lock. + A pending metadata lock request. + A pending lock request or a granted metadata lock share the same abstract + base but are presented individually because they have different allocation + sites and hence different lifetimes. The allocation of lock requests is + controlled from outside of the MDL subsystem, while allocation of granted + locks (tickets) is controlled within the MDL subsystem. */ -struct MDL_LOCK_DATA_lock +struct MDL_LOCK_REQUEST { - static inline MDL_LOCK_DATA **next_ptr(MDL_LOCK_DATA *l) - { - return &l->next_lock; - } - static inline MDL_LOCK_DATA ***prev_ptr(MDL_LOCK_DATA *l) - { - return &l->prev_lock; - } + /** Type of metadata lock. */ + enum enum_mdl_type type; + + /** + Pointers for participating in the list of lock requests for this context. + */ + MDL_LOCK_REQUEST *next_in_context; + MDL_LOCK_REQUEST **prev_in_context; + /** A lock is requested based on a fully qualified name and type. */ + MDL_KEY key; + + /** + Pointer to the lock ticket object for this lock request. + Valid only if this lock request is satisfied. + */ + MDL_LOCK_TICKET *ticket; +}; + + +/** + A granted metadata lock. + + @warning MDL_LOCK_TICKET members are private to the MDL subsystem. + + @note Multiple shared locks on a same object are represented by a + single ticket. The same does not apply for other lock types. +*/ + +struct MDL_LOCK_TICKET +{ + /** Type of metadata lock. */ + enum enum_mdl_type type; + /** State of the metadata lock ticket. */ + enum enum_mdl_state state; + + /** + Pointers for participating in the list of lock requests for this context. + */ + MDL_LOCK_TICKET *next_in_context; + MDL_LOCK_TICKET **prev_in_context; + /** + Pointers for participating in the list of satisfied/pending requests + for the lock. + */ + MDL_LOCK_TICKET *next_in_lock; + MDL_LOCK_TICKET **prev_in_lock; + /** Context of the owner of the metadata lock ticket. */ + MDL_CONTEXT *ctx; + + /** Pointer to the lock object for this lock ticket. */ + MDL_LOCK *lock; }; @@ -134,7 +195,24 @@ struct MDL_LOCK_DATA_lock struct MDL_CONTEXT { - I_P_List <MDL_LOCK_DATA, MDL_LOCK_DATA_context> locks; + typedef I_P_List<MDL_LOCK_REQUEST, + I_P_List_adapter<MDL_LOCK_REQUEST, + &MDL_LOCK_REQUEST::next_in_context, + &MDL_LOCK_REQUEST::prev_in_context> > + Request_list; + + typedef Request_list::Iterator Request_iterator; + + typedef I_P_List<MDL_LOCK_TICKET, + I_P_List_adapter<MDL_LOCK_TICKET, + &MDL_LOCK_TICKET::next_in_context, + &MDL_LOCK_TICKET::prev_in_context> > + Ticket_list; + + typedef Ticket_list::Iterator Ticket_iterator; + + Request_list requests; + Ticket_list tickets; bool has_global_shared_lock; THD *thd; }; @@ -149,96 +227,80 @@ void mdl_context_backup_and_reset(MDL_CONTEXT *ctx, MDL_CONTEXT *backup); void mdl_context_restore(MDL_CONTEXT *ctx, MDL_CONTEXT *backup); void mdl_context_merge(MDL_CONTEXT *target, MDL_CONTEXT *source); -/** Maximal length of key for metadata locking subsystem. */ -#define MAX_MDLKEY_LENGTH (4 + NAME_LEN + 1 + NAME_LEN + 1) - -void mdl_init_lock(MDL_LOCK_DATA *lock_data, char *key, int type, - const char *db, const char *name); -MDL_LOCK_DATA *mdl_alloc_lock(int type, const char *db, const char *name, - MEM_ROOT *root); -void mdl_add_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data); -void mdl_remove_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data); -void mdl_remove_all_locks(MDL_CONTEXT *context); +void mdl_request_init(MDL_LOCK_REQUEST *lock_req, unsigned char type, + const char *db, const char *name); +MDL_LOCK_REQUEST *mdl_request_alloc(unsigned char type, const char *db, + const char *name, MEM_ROOT *root); +void mdl_request_add(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req); +void mdl_request_remove(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req); +void mdl_request_remove_all(MDL_CONTEXT *context); /** Set type of lock request. Can be only applied to pending locks. */ -inline void mdl_set_lock_type(MDL_LOCK_DATA *lock_data, enum_mdl_type lock_type) +inline void mdl_request_set_type(MDL_LOCK_REQUEST *lock_req, enum_mdl_type lock_type) { - DBUG_ASSERT(lock_data->state == MDL_INITIALIZED); - lock_data->type= lock_type; + DBUG_ASSERT(lock_req->ticket == NULL); + lock_req->type= lock_type; } -bool mdl_acquire_shared_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data, +bool mdl_acquire_shared_lock(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req, bool *retry); bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context); bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data); + MDL_LOCK_TICKET *ticket); bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data, + MDL_LOCK_REQUEST *lock_req, bool *conflict); bool mdl_acquire_global_shared_lock(MDL_CONTEXT *context); bool mdl_wait_for_locks(MDL_CONTEXT *context); -void mdl_release_locks(MDL_CONTEXT *context); -void mdl_release_and_remove_all_locks_for_name(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data); -void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data); +void mdl_ticket_release_all(MDL_CONTEXT *context); +void mdl_ticket_release_all_for_name(MDL_CONTEXT *context, + MDL_LOCK_TICKET *ticket); +void mdl_ticket_release(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket); void mdl_downgrade_exclusive_lock(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data); + MDL_LOCK_TICKET *ticket); void mdl_release_global_shared_lock(MDL_CONTEXT *context); -bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, int type, const char *db, - const char *name); -bool mdl_is_lock_owner(MDL_CONTEXT *context, int type, const char *db, - const char *name); +bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, unsigned char type, + const char *db, const char *name); +bool mdl_is_lock_owner(MDL_CONTEXT *context, unsigned char type, + const char *db, const char *name); -bool mdl_has_pending_conflicting_lock(MDL_LOCK_DATA *lock_data); +bool mdl_has_pending_conflicting_lock(MDL_LOCK_TICKET *ticket); inline bool mdl_has_locks(MDL_CONTEXT *context) { - return !context->locks.is_empty(); + return !context->tickets.is_empty(); } - -/** - Get iterator for walking through all lock requests in the context. -*/ - -inline I_P_List_iterator<MDL_LOCK_DATA, MDL_LOCK_DATA_context> -mdl_get_locks(MDL_CONTEXT *ctx) +inline MDL_LOCK_TICKET *mdl_savepoint(MDL_CONTEXT *ctx) { - I_P_List_iterator<MDL_LOCK_DATA, MDL_LOCK_DATA_context> result(ctx->locks); - return result; + return ctx->tickets.head(); } -/** - Give metadata lock request object for the table get table definition - cache key corresponding to it. - - @param lock_data [in] Lock request object for the table. - @param key [out] LEX_STRING object where table definition cache key - should be put. +void mdl_rollback_to_savepoint(MDL_CONTEXT *ctx, + MDL_LOCK_TICKET *mdl_savepoint); - @note This key will have the same life-time as this lock request object. - - @note This is yet another place where border between MDL subsystem and the - rest of the server is broken. OTOH it allows to save some CPU cycles - and memory by avoiding generating these TDC keys from table list. +/** + Get iterator for walking through all lock requests in the context. */ -inline void mdl_get_tdc_key(MDL_LOCK_DATA *lock_data, LEX_STRING *key) +inline MDL_CONTEXT::Request_iterator +mdl_get_requests(MDL_CONTEXT *ctx) { - key->str= lock_data->key + 4; - key->length= lock_data->key_length - 4; + MDL_CONTEXT::Request_iterator result(ctx->requests); + return result; } +void mdl_get_tdc_key(MDL_LOCK_TICKET *ticket, LEX_STRING *key); typedef void (* mdl_cached_object_release_hook)(void *); -void* mdl_get_cached_object(MDL_LOCK_DATA *lock_data); -void mdl_set_cached_object(MDL_LOCK_DATA *lock_data, void *cached_object, +void *mdl_get_cached_object(MDL_LOCK_TICKET *ticket); +void mdl_set_cached_object(MDL_LOCK_TICKET *ticket, void *cached_object, mdl_cached_object_release_hook release_hook); |