#At file:///home/sven/bzr/w3584-ugid/trunk/ based on revid:sven.sandberg@stripped
3338 Sven Sandberg 2011-05-03
x
added:
sql/zgroup_cache.cc
sql/zgroup_execution.cc
modified:
libmysqld/CMakeLists.txt
sql/CMakeLists.txt
sql/binlog.cc
sql/binlog.h
sql/share/errmsg-utf8.txt
sql/sys_vars.cc
sql/zgroup_log_state.cc
sql/zgroup_set.cc
sql/zgroups.h
sql/zowned_groups.cc
sql/zsid_map.cc
unittest/gunit/rpl_group_set-t.cc
=== modified file 'libmysqld/CMakeLists.txt'
--- a/libmysqld/CMakeLists.txt 2011-04-28 16:16:51 +0000
+++ b/libmysqld/CMakeLists.txt 2011-05-03 17:52:32 +0000
@@ -71,7 +71,8 @@ SET(SQL_EMBEDDED_SOURCES
../sql/zugid_specification.cc
../sql/zgroup_log_state.cc
../sql/zowned_groups.cc
- ${IMPORTED_SOURCES}
+ ../sql/zgroup_cache.cc
+ ${IMPORTED_SOURCES}
)
=== modified file 'sql/CMakeLists.txt'
--- a/sql/CMakeLists.txt 2011-04-28 16:16:51 +0000
+++ b/sql/CMakeLists.txt 2011-05-03 17:52:32 +0000
@@ -185,7 +185,7 @@ TARGET_LINK_LIBRARIES(sql ${MYSQLD_STATI
${SSL_LIBRARIES})
SET (BINLOG_SOURCE zuuid.cc zsid_map.cc zgroup_set.cc zugid_specification.cc
- zgroup_log_state.cc zowned_groups.cc
+ zgroup_log_state.cc zowned_groups.cc zgroup_cache.cc
log_event.cc log_event_old.cc binlog.cc sql_binlog.cc
rpl_filter.cc rpl_record.cc rpl_record_old.cc rpl_utility.cc
rpl_injector.cc)
=== modified file 'sql/binlog.cc'
--- a/sql/binlog.cc 2011-04-04 08:47:25 +0000
+++ b/sql/binlog.cc 2011-05-03 17:52:32 +0000
@@ -1557,6 +1557,9 @@ MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_
checksum_alg_reset(BINLOG_CHECKSUM_ALG_UNDEF),
relay_log_checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF),
description_event_for_exec(0), description_event_for_queue(0)
+#ifdef HAVE_UGID
+ , sid_map(), group_log_state(&sid_map, &group_log_state_init_status)
+#endif
{
/*
We don't want to initialize locks here as such initialization depends on
@@ -1568,6 +1571,7 @@ MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_
bzero((char*) &index_file, sizeof(index_file));
bzero((char*) &purge_index_file, sizeof(purge_index_file));
bzero((char*) &crash_safe_index_file, sizeof(crash_safe_index_file));
+ DBUG_ASSERT(group_log_state_init_status == GS_SUCCESS);
}
/* this is called only once */
=== modified file 'sql/binlog.h'
--- a/sql/binlog.h 2011-04-28 16:16:51 +0000
+++ b/sql/binlog.h 2011-05-03 17:52:32 +0000
@@ -309,7 +309,9 @@ public:
inline uint32 get_open_count() { return open_count; }
#ifdef HAVE_UGID
+ enum_group_status group_log_state_init_status;
Sid_map sid_map;
+ Group_log_state group_log_state;
#endif
};
=== modified file 'sql/share/errmsg-utf8.txt'
--- a/sql/share/errmsg-utf8.txt 2011-04-26 10:48:53 +0000
+++ b/sql/share/errmsg-utf8.txt 2011-05-03 17:52:32 +0000
@@ -6501,3 +6501,6 @@ ER_CHANGE_RPL_INFO_REPOSITORY_FAILURE
ER_VARIABLE_NOT_SETTABLE_IN_SUB_STATEMENT
eng "The system variable %s can only be set in top-level statements."
+
+ER_VARIABLE_NOT_SETTABLE_IN_TRANSACTION
+ eng "The system variable %s can not be set when there is an ongoing transaction."
=== modified file 'sql/sys_vars.cc'
--- a/sql/sys_vars.cc 2011-04-28 16:16:51 +0000
+++ b/sql/sys_vars.cc 2011-05-03 17:52:32 +0000
@@ -363,21 +363,30 @@ static bool check_has_super(sys_var *sel
}
#ifdef HAVE_UGID
-static bool is_top_level_stmt(sys_var *self, THD *thd, set_var *var)
+static bool check_top_level_stmt(sys_var *self, THD *thd, set_var *var)
{
if (thd->in_sub_stmt)
{
my_error(ER_VARIABLE_NOT_SETTABLE_IN_SUB_STATEMENT, MYF(0), var->var->name);
- return false;
+ return true;
}
- return true;
+ return false;
}
-static bool is_top_level_stmt_and_super(sys_var *self, THD *thd, set_var *var)
+static bool check_top_level_stmt_and_super(sys_var *self, THD *thd, set_var *var)
{
- // settable only by super in top-level statements
return (check_has_super(self, thd, var) ||
- !is_top_level_stmt(self, thd, var));
+ check_top_level_stmt(self, thd, var));
+}
+
+static bool check_outside_transaction(sys_var *self, THD *thd, set_var *var)
+{
+ if (thd->in_active_multi_stmt_transaction())
+ {
+ my_error(ER_VARIABLE_NOT_SETTABLE_IN_TRANSACTION, MYF(0), var->var->name);
+ return true;
+ }
+ return false;
}
#endif
@@ -3424,37 +3433,43 @@ static Sys_var_tz Sys_time_zone(
#ifdef HAVE_UGID
-/*static bool fix_ugid_next(sys_var *self, THD *thd, enum_var_type type)
-{
-
-}*/
-static Sys_var_ugid_specification Sys_ugid_next(
- "ugid_next",
- "The Universal Group Identifier for the following statement.",
- SESSION_ONLY(ugid_next), NO_CMD_LINE,
- DEFAULT("AUTOMATIC"), NO_MUTEX_GUARD,
- NOT_IN_BINLOG, ON_CHECK(is_top_level_stmt_and_super));
+static bool check_ugid_next_list(sys_var *self, THD *thd, set_var *var)
+{
+ if (check_top_level_stmt_and_super(self, thd, var) ||
+ check_outside_transaction(self, thd, var))
+ return true;
+ // TODO: check that there is no ongoing super-group (i.e., check
+ // that we don't own any groups)
+ return false;
+}
static Sys_var_group_set Sys_ugid_next_list(
"ugid_next_list",
"The set of groups that will be part of the following super-group.",
SESSION_ONLY(ugid_next_list), NO_CMD_LINE,
DEFAULT(NULL), NO_MUTEX_GUARD,
- NOT_IN_BINLOG, ON_CHECK(is_top_level_stmt_and_super));
+ NOT_IN_BINLOG, ON_CHECK(check_ugid_next_list));
+
+static Sys_var_ugid_specification Sys_ugid_next(
+ "ugid_next",
+ "The Universal Group Identifier for the following statement.",
+ SESSION_ONLY(ugid_next), NO_CMD_LINE,
+ DEFAULT("AUTOMATIC"), NO_MUTEX_GUARD,
+ NOT_IN_BINLOG, ON_CHECK(check_top_level_stmt_and_super));
static Sys_var_mybool Sys_ugid_end(
"ugid_end",
- "If true, the next statement is the last one in an ending sub-group.",
+ "If true, the next statement is the last one in a group.",
SESSION_ONLY(ugid_end), NO_CMD_LINE,
DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG,
- ON_CHECK(is_top_level_stmt_and_super));
+ ON_CHECK(check_top_level_stmt_and_super));
static Sys_var_mybool Sys_ugid_commit(
"ugid_commit",
- "If true, the next statement is the last one in an ending sub-group.",
+ "If true, the next statement is the last one in a super-group.",
SESSION_ONLY(ugid_commit), NO_CMD_LINE,
DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG,
- ON_CHECK(is_top_level_stmt_and_super));
+ ON_CHECK(check_top_level_stmt_and_super));
#endif
=== added file 'sql/zgroup_cache.cc'
--- a/sql/zgroup_cache.cc 1970-01-01 00:00:00 +0000
+++ b/sql/zgroup_cache.cc 2011-05-03 17:52:32 +0000
@@ -0,0 +1,153 @@
+/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+#include "zgroups.h"
+
+#ifdef HAVE_UGID
+
+/**
+ Creates the used_sidnos bitmap, with space for the given max_sidno.
+
+ @param max_sidno The maximum sidno that will be supported by this Group_cache.
+ @return GS_SUCCESS or GS_OUT_OF_MEMORY.
+*/
+enum_group_status Group_cache::allocate_bitmap(rpl_sidno max_sidno)
+{
+ return my_bitmap_init(&used_sidnos, NULL,
+ /* round up to nearest sizeof(my_bitmap_map) bytes */
+ (max_sidno + sizeof(my_bitmap_map) * CHAR_BIT - 1) &
+ ~(sizeof(my_bitmap_map) * CHAR_BIT - 1), 0) ?
+ GS_ERROR_OUT_OF_MEMORY : GS_SUCCESS;
+}
+
+Group_cache::Group_cache(rpl_sidno default_max_sidno, enum_group_status *error)
+{
+ DBUG_ENTER("Group_cache::Group_cache");
+ my_init_dynamic_array(&groups, sizeof(Cached_subgroup), 8, 8);
+ *error= allocate_bitmap(default_max_sidno);
+ DBUG_VOID_RETURN;
+}
+
+enum_group_status Group_cache::append(Cached_subgroup *group)
+{
+ DBUG_ENTER("Group_cache::append");
+ if (insert_dynamic(&groups, group) == 0)
+ DBUG_RETURN(GS_ERROR_OUT_OF_MEMORY);
+ bitmap_set_bit(&used_sidnos, group->sidno);
+ DBUG_RETURN(GS_SUCCESS);
+}
+
+enum_group_status Group_cache::clear(rpl_sidno max_sidno)
+{
+ DBUG_ENTER("Group_cache::clear");
+ groups.elements= 0;
+ if (max_sidno > used_sidnos.n_bits)
+ {
+ bitmap_free(&used_sidnos);
+ DBUG_RETURN(allocate_bitmap(max_sidno));
+ }
+ DBUG_RETURN(GS_SUCCESS);
+}
+
+/**
+ Return true if this Group_cache contains the given group.
+
+ @param sidno The SIDNO of the group to check.
+ @param gno The GNO of the group to check.
+ @retval true The group exists in this cache.
+ @retval false The group does not exist in this cache.
+*/
+bool Group_cache::contains_group(rpl_sidno sidno, rpl_gno gno) const
+{
+ for (int i= 0; i < n_subgroups; i++)
+ {
+ Cached_subgroup *cs= get_unsafe_pointer(i);
+ if (cs->gno == gno && cs->sidno= sidno)
+ return true;
+ }
+ return false;
+}
+
+enum_group_status
+Group_cache::add_dummy_group_for_missing_group(Group_log_state *gls,
+ rpl_sidno sidno, rpl_gno gno)
+{
+ if (!gls->is_ended(sidno, gno) && !gls->is_partial(sidno, gno) &&
+ !contains_group(sidno, gno))
+ {
+ Cached_subgroup cs=
+ {
+ DUMMY_SUBGROUP, sidno, gno,
+ 0/*binlog_pos*/, 0/*binlog_length*/,
+ 0/*binlog_offset_after_last_statement*/,
+ false/*group_end*/
+ };
+ GROUP_STATUS_THROW(append(&cs));
+ }
+}
+
+enum_group_status
+Group_cache::add_dummy_groups_for_missing_groups(Group_log_state *gls,
+ Group_set *group_set)
+{
+ DBUG_ENTER("Group_cache::add_dummy_groups_for_missing_groups");
+ Group_set::Group_iterator git(group_set);
+ /*
+ @todo: This algorithm is
+ O(n_groups_in_cache*n_groups_in_group_set) because contains_group
+ is O(n_groups_in_cache). We can optimize this to
+ O(n_groups_in_cache+n_groups_in_group_set), as follows: create a
+ HASH and copy all groups from the cache to the HASH. Then use the
+ HASH to detect if the group is in the cache or not. This has some
+ overhead so should only be used if
+ n_groups_in_cache*n_groups_in_group_set is significantly bigger
+ than n_groups_in_cache+n_groups_in_group_set elements. /Sven
+ */
+ for (Group g= git.get(); g.sidno; git.next())
+ GROUP_STATUS_THROW(add_dummy_group_for_missing_group(gls, g.sidno, g.gno));
+ DBUG_RETURN(GS_SUCCESS);
+}
+
+enum_group_status Group_cache::update_group_log_state(Group_log_state *gls)
+{
+ gls->lock_sidnos(&used_sidnos);
+
+ enum_group_status ret= GS_SUCCESS;
+ bool updated= false;
+ int n_subgroups= get_n_subgroups();
+
+ for (int i= 0; i < n_subgroups; i++)
+ {
+ Cached_subgroup *cs= get_unsafe_pointer(i);
+ if (cs->group_end)
+ {
+ updated= true;
+ ret= gls->end_group(cs->sidno, cs->gno);
+ if (ret != GS_SUCCESS)
+ break;
+ }
+ else if (!gls->mark_partial(cs->sidno, cs->gno))
+ updated= true;
+ }
+
+ if (updated)
+ gls->broadcast_sidnos(&used_sidnos);
+ else
+ gls->unlock_sidnos(&used_sidnos);
+
+ return ret;
+}
+
+#endif
=== added file 'sql/zgroup_execution.cc'
--- a/sql/zgroup_execution.cc 1970-01-01 00:00:00 +0000
+++ b/sql/zgroup_execution.cc 2011-05-03 17:52:32 +0000
@@ -0,0 +1,41 @@
+/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+#include "zgroups.h"
+
+#ifdef HAVE_UGID
+
+void flush_cache(THD *thd, Group_cache *gc)
+{
+ Group_set *ugid_next_list= thd->variables.ugid_next_list.group_set;
+ if (ugid_next_list != NULL)
+ gc->add_dummy_groups_for_missing_groups(&mysql_bin_log.group_log_state,
+ ugid_next_list);
+ else
+ {
+ Ugid_specification *ugid_next= &thd->variables.ugid_next;
+ if (ugid_next->type == UGID)
+ gc->add_dummy_group_for_missing_group(&mysql_bin_log.group_log_state,
+ ugid_next->sidno, ugid_next->gno);
+ }
+ /*
+ @todo: We have not yet implemented the group log. When that is
+ done, uncomment the next line. /Sven
+ */
+ // gc->write_to_log();
+ gc->update_group_log_state(&mysql_bin_log.group_log_state);
+}
+
+#endif
=== modified file 'sql/zgroup_log_state.cc'
--- a/sql/zgroup_log_state.cc 2011-04-28 16:16:51 +0000
+++ b/sql/zgroup_log_state.cc 2011-05-03 17:52:32 +0000
@@ -17,18 +17,6 @@
#ifdef HAVE_UGID
-Group_log_state::enum_group_completeness
-Group_log_state::get_group_completeness(rpl_sidno sidno, rpl_gno gno,
- rpl_thread_id *owner) const
-{
- if (ended_groups.contains(sidno, gno))
- return ENDED;
- else if ((*owner= owned_groups.get_owner(sidno, gno)) != NO_THREAD)
- return OWNED;
- else
- return FREE;
-}
-
enum_group_status
Group_log_state::acquire_ownership(rpl_sidno sidno, rpl_gno gno,
rpl_thread_id owner)
@@ -46,7 +34,7 @@ enum_group_status Group_log_state::end_g
DBUG_RETURN(ended_groups.add(sidno, gno));
}
-rpl_gno Group_log_state::end_automatic_group(rpl_sidno sidno)
+rpl_gno Group_log_state::get_automatic_gno(rpl_sidno sidno)
{
DBUG_ENTER("Group_log_state::end_automatic_group");
ended_groups.ensure_sidno(sidno);
@@ -67,4 +55,28 @@ rpl_gno Group_log_state::end_automatic_g
}
}
+void Group_log_state::lock_sidnos(MY_BITMAP *set)
+{
+ rpl_sidno n_bits= set->n_bits;
+ for (rpl_sidno i= 0; i < n_bits; i++)
+ if (bitmap_is_set(set, i))
+ lock_sidno(i);
+}
+
+void Group_log_state::unlock_sidnos(MY_BITMAP *set)
+{
+ rpl_sidno n_bits= set->n_bits;
+ for (rpl_sidno i= 0; i < n_bits; i++)
+ if (bitmap_is_set(set, i))
+ unlock_sidno(i);
+}
+
+void Group_log_state::broadcast_sidnos(MY_BITMAP *set)
+{
+ rpl_sidno n_bits= set->n_bits;
+ for (rpl_sidno i= 0; i < n_bits; i++)
+ if (bitmap_is_set(set, i))
+ broadcast_sidno(i);
+}
+
#endif /* HAVE_UGID */
=== modified file 'sql/zgroup_set.cc'
--- a/sql/zgroup_set.cc 2011-04-28 16:16:51 +0000
+++ b/sql/zgroup_set.cc 2011-05-03 17:52:32 +0000
@@ -81,12 +81,6 @@ Group_set::~Group_set()
DBUG_VOID_RETURN;
}
-/**
- Allocates space for the given sidno in the intervals array.
-
- @param sidno The SIDNO.
- @return GS_SUCCESS or GS_ERROR_OUT_OF_MEMORY.
-*/
enum_group_status Group_set::ensure_sidno(rpl_sidno sidno)
{
DBUG_ENTER("Group_set::ensure_sidno");
=== modified file 'sql/zgroups.h'
--- a/sql/zgroups.h 2011-04-28 16:16:51 +0000
+++ b/sql/zgroups.h 2011-05-03 17:52:32 +0000
@@ -29,6 +29,7 @@
#include "mysqld.h"
#include "hash.h"
+#include "lf.h"
typedef int64 rpl_gno;
typedef int32 rpl_sidno;
@@ -68,6 +69,16 @@ const rpl_thread_id NO_THREAD= -1;
const int MAX_GNO_TEXT_LENGTH= 19;
/**
+ Holds information about a group: the sidno and the gno.
+*/
+class Group
+{
+public:
+ rpl_sidno sidno;
+ rpl_gno gno;
+};
+
+/**
Parse a GNO from a string.
@param s Pointer to the string. *s will advance to the end of the
@@ -101,11 +112,11 @@ public:
/**
Copies the given 16-byte data to this UUID.
*/
- void copy_from(const unsigned char *data) { memcpy(uuid, data, BYTE_LENGTH); };
+ void copy_from(const unsigned char *data) { memcpy(uuid, data, BYTE_LENGTH); }
/**
Copies the given UUID object to this UUID.
*/
- void copy_from(const Uuid *data) { copy_from(data->uuid); };
+ void copy_from(const Uuid *data) { copy_from(data->uuid); }
/**
Returns true if this UUID is equal the given UUID.
*/
@@ -209,43 +220,100 @@ public:
*/
int get_max_sidno() const { return _sidno_to_sid.elements; }
- void lock(rpl_sidno sidno)
+private:
+ class Node
{
- mysql_mutex_lock(&sidno_to_node(sidno)->mutex);
+ public:
+ rpl_sidno sidno;
+ rpl_sid sid;
};
- void unlock(rpl_sidno sidno)
+
+ inline Node *sidno_to_node(rpl_sidno sidno) const
{
- mysql_mutex_unlock(&sidno_to_node(sidno)->mutex);
+ return *dynamic_element(&_sidno_to_sid, sidno - 1, Node **);
}
- void broadcast(rpl_sidno sidno)
+
+ DYNAMIC_ARRAY _sidno_to_sid;
+ HASH _sid_to_sidno;
+ DYNAMIC_ARRAY _sorted;
+};
+
+class Mutex_cond_array
+{
+public:
+ Mutex_cond_array()
{
- mysql_cond_broadcast(&sidno_to_node(sidno)->cond);
+ lf_dynarray_init(&array, sizeof(Mutex_cond));
+ mysql_mutex_init(NULL, &internal_update_mutex, NULL);
}
- void wait_for_update(rpl_sidno sidno)
+ ~Mutex_cond_array()
{
- DBUG_ENTER("Group_set::wait_for_update");
- Node *node= sidno_to_node(sidno);
- mysql_cond_wait(&node->cond, &node->mutex);
+ for (int i= 0; i < max_index; i++)
+ {
+ Mutex_cond *mutex_cond= get_mutex_cond(i);
+ if (mutex_cond)
+ {
+ mysql_mutex_destroy(&mutex_cond->mutex);
+ mysql_cond_destroy(&mutex_cond->cond);
+ }
+ }
+ lf_dynarray_destroy(&array);
+ mysql_mutex_destroy(&internal_update_mutex);
+ }
+ inline void lock(int n)
+ {
+ mysql_mutex_lock(&get_mutex_cond(n)->mutex);
+ }
+ inline void unlock(int n)
+ {
+ mysql_mutex_unlock(&get_mutex_cond(n)->mutex);
+ }
+ inline void broadcast(int n)
+ {
+ mysql_cond_broadcast(&get_mutex_cond(n)->cond);
+ }
+ inline void wait(int n)
+ {
+ DBUG_ENTER("Mutex_cond_array::wait");
+ Mutex_cond *mutex_cond= get_mutex_cond(n);
+ mysql_cond_wait(&mutex_cond->cond, &mutex_cond->mutex);
DBUG_VOID_RETURN;
}
private:
- class Node
+ class Mutex_cond
{
public:
- rpl_sidno sidno;
- rpl_sid sid;
mysql_mutex_t mutex;
mysql_cond_t cond;
};
+ mysql_mutex_t internal_update_mutex;
+ int max_index;
- inline Node *sidno_to_node(rpl_sidno sidno) const
+ LF_DYNARRAY array;
+ inline Mutex_cond *get_mutex_cond(int n)
{
- return *dynamic_element(&_sidno_to_sid, sidno - 1, Node **);
+ Mutex_cond *mutex_cond= (Mutex_cond *)lf_dynarray_value(&array, n);
+ if (!mutex_cond)
+ {
+ mysql_mutex_lock(&internal_update_mutex);
+ // try again: maybe another thread created this mutex_cond just before
+ // we took the global mutex
+ mutex_cond= (Mutex_cond *)lf_dynarray_value(&array, n);
+ if (!mutex_cond)
+ {
+ mutex_cond= (Mutex_cond *)lf_dynarray_lvalue(&array, n);
+ if (mutex_cond)
+ {
+ mysql_mutex_init(NULL, &mutex_cond->mutex, NULL);
+ mysql_cond_init(NULL, &mutex_cond->cond, NULL);
+ if (n > max_index)
+ max_index= n;
+ }
+ }
+ mysql_mutex_unlock(&internal_update_mutex);
+ }
+ return mutex_cond;
}
-
- DYNAMIC_ARRAY _sidno_to_sid;
- HASH _sid_to_sidno;
- DYNAMIC_ARRAY _sorted;
};
/**
@@ -334,6 +402,14 @@ public:
*/
rpl_sidno get_max_sidno() const { return intervals.elements - 1; }
/**
+ Allocates space for all sidnos up to the given sidno in the array of intervals.
+ The sidno must exist in the Sid_map associated with this Group_set.
+
+ @param sidno The SIDNO.
+ @return GS_SUCCESS or GS_ERROR_OUT_OF_MEMORY.
+ */
+ enum_group_status ensure_sidno(rpl_sidno sidno);
+ /**
Returns true if this Group_set is equal to the other Group_set.
*/
bool equals(const Group_set *other) const;
@@ -403,37 +479,37 @@ public:
};
/**
- Iterator over intervals.
+ Iterator over intervals for a given SIDNO.
- The iterator always points to a current_elem. As a special case,
- the current_interval may be the empty position after the last
- interval.
+ The iterator always points to a current_elem, which is either one
+ of the intervals for the SIDNO or the empty position after the
+ last interval.
*/
class Interval_iterator
{
public:
/**
- Construct a new iterator where current_elem is the first element
- of the list.
+ Construct a new iterator where current_elem is the first
+ interval for the given sidno.
*/
Interval_iterator(const Group_set *group_set, rpl_sidno sidno)
- : p(dynamic_element(&group_set->intervals, sidno, Interval **))
{
DBUG_ASSERT(sidno >= 1 && sidno <= group_set->get_max_sidno());
+ init(group_set, sidno);
}
- /**
- Construct a new iterator over the free intervals.
- */
- Interval_iterator(const Group_set *group_set)
- : p(dynamic_element(&group_set->intervals, 0, Interval **)) {}
+ /** Construct a new iterator over the free intervals. */
+ Interval_iterator(const Group_set *group_set) { init(group_set, 0); }
+ /** Reset this iterator. */
+ inline void init(const Group_set *group_set, rpl_sidno sidno)
+ { p= get_pointer_to_head(group_set, sidno); }
/** Advance current_elem one step. */
inline void next()
{
DBUG_ASSERT(*p != NULL);
p= &(*p)->next;
- };
+ }
/** Insert the given element before current_elem. */
- inline void insert(Interval *iv) { iv->next= *p; set(iv); };
+ inline void insert(Interval *iv) { iv->next= *p; set(iv); }
/** Remove current_elem. */
inline void remove(Group_set *group_set)
{
@@ -443,18 +519,79 @@ public:
set(next);
}
/** Return current_elem. */
- inline Interval *get() const { return *p; };
+ inline Interval *get() const { return *p; }
/** Set current_elem to the given Interval but do not touch the next pointer of the given Interval. */
- inline void set(Interval *iv) { *p= iv; };
+ inline void set(Interval *iv) { *p= iv; }
private:
+ inline Interval **get_pointer_to_head(const Group_set *group_set,
+ rpl_sidno sidno)
+ { return dynamic_element(&group_set->intervals, 0, Interval **); }
/**
- This holds the address of the 'next' pointer of the previous
- element, or the address of the initial pointer into the list, if
- the current element is the first element.
+ Holds the address of the 'next' pointer of the previous element,
+ or the address of the initial pointer into the list, if the
+ current element is the first element.
*/
Interval **p;
};
- enum_group_status ensure_sidno(rpl_sidno sidno);
+
+ /** Iterator over all groups in this Group_set. */
+ class Group_iterator
+ {
+ public:
+ Group_iterator(Group_set *gs)
+ : group_set(gs), sidno(0), ivit(gs) { next_sidno(); }
+ /** Advance to next group. */
+ inline void next()
+ {
+ DBUG_ASSERT(gno > 0 && sidno > 0);
+ // go to next group in current interval
+ gno++;
+ // end of interval? then go to next interval for this sidno
+ if (gno == ivit.get()->end)
+ {
+ ivit.next();
+ Interval *iv= ivit.get();
+ // last interval for this sidno? then go to next sidno
+ if (iv == NULL)
+ {
+ next_sidno();
+ // last sidno? then don't try more
+ if (sidno == 0)
+ return;
+ iv= ivit.get();
+ }
+ gno= iv->start;
+ }
+ }
+ /** Return next group, or {0,0} if we reached the end. */
+ inline Group get()
+ {
+ Group ret= { sidno, gno };
+ return ret;
+ }
+ private:
+ /** Find next sidno that has one or more intervals. */
+ inline void next_sidno()
+ {
+ Interval *iv;
+ do {
+ sidno++;
+ if (sidno > group_set->get_max_sidno())
+ {
+ sidno= 0;
+ gno= 0;
+ return;
+ }
+ ivit.init(group_set, sidno);
+ iv= ivit.get();
+ } while (iv == NULL);
+ gno= iv->start;
+ }
+ Group_set *group_set;
+ rpl_sidno sidno;
+ rpl_gno gno;
+ Interval_iterator ivit;
+ };
private:
static const int CHUNK_GROW_SIZE = 8;
@@ -513,19 +650,14 @@ public:
};
/**
- Holds information about a group: the sidno and the gno.
-*/
-class Group
-{
-public:
- rpl_sidno sidno;
- rpl_gno gno;
-};
-
-/**
Represents the set of groups that are owned by some thread.
- This has the form of a map from (SID, GNO) to thread_id.
+ This consists of all partial groups and a subset of the unlogged
+ groups. Each group has a flag that indicates whether it is partial
+ or not.
+
+ The internal representation is a map (HASH) from (SID, GNO) to
+ (thread_id, bool).
*/
class Owned_groups
{
@@ -536,6 +668,8 @@ public:
rpl_thread_id get_owner(rpl_sidno sidno, rpl_gno gno) const;
enum_group_status set_owner(rpl_sidno sidno, rpl_gno gno, rpl_thread_id owner);
void remove(rpl_sidno sidno, rpl_gno gno);
+ bool mark_partial(rpl_sidno sidno, rpl_gno gno);
+ bool is_partial(rpl_sidno sidno, rpl_gno gno) const;
private:
class Node
@@ -543,6 +677,7 @@ private:
public:
Group group;
rpl_thread_id owner;
+ bool is_partial;
};
Node *get_node(rpl_sidno sidno, rpl_gno gno) const
{
@@ -561,28 +696,47 @@ private:
class Group_log_state
{
public:
- enum enum_group_completeness
- {
- ENDED, OWNED, FREE
- };
Group_log_state(Sid_map *sid_map, enum_group_status *error)
- : ended_groups(sid_map, error) {};
+ : ended_groups(sid_map, error) {}
+ /**
+ Returns true if the given group is ended.
+
+ @param sidno The SIDNO to check.
+ @param gno The GNO to check.
+ @retval true The group is ended in the group log.
+ @retval false The group is partial or unlogged in the group log.
+ */
+ bool is_ended(rpl_sidno sidno, rpl_gno gno) const
+ { return ended_groups.contains(sidno, gno); }
+ /**
+ Returns true if the given group is partial.
+
+ @param sidno The SIDNO to check.
+ @param gno The GNO to check.
+ @retval true The group is partial in the group log.
+ @retval false The group is ended or unlogged in the group log.
+ */
+ bool is_partial(rpl_sidno sidno, rpl_gno gno) const
+ { return owned_groups.is_partial(sidno, gno); }
/**
- Returns the status for the given group: ENDED, PARTIAL, or UNLOGGED.
+ Returns the owner of the given group.
@param sidno The SIDNO to check.
@param gno The GNO to check.
- @param owner If the status is PARTIAL, the owner of the group will
- be stored here.
+ @retval NO_THREAD if the group is not owned.
+ @retval other ID of the thread that owns the group.
+ */
+ rpl_thread_id get_owner(rpl_sidno sidno, rpl_gno gno) const
+ { return owned_groups.get_owner(sidno, gno); }
+ /**
+ Marks the given group as partial.
- @retval ENDED The group has been ended in the group log.
- @retval OWNED The group is partial or unlogged in the group log,
- and is owned by some client.
- @retval FREE The group is unlogged in the group log and is not
- owned by any client.
+ @param sidno The SIDNO of the group.
+ @param gno The GNO of the group.
+ @return The value of is_partial() before this call.
*/
- enum_group_completeness get_group_completeness(rpl_sidno sidno, rpl_gno gno,
- rpl_thread_id *owner) const;
+ bool mark_partial(rpl_sidno sidno, rpl_gno gno)
+ { return owned_groups.mark_partial(sidno, gno); }
/**
Acquires ownership of the given group, on behalf of the given thread.
@@ -612,9 +766,18 @@ public:
@retval negative the numeric value of GS_OUT_OF_MEMORY
@retval other The GNO for the group.
*/
- rpl_gno end_automatic_group(rpl_sidno sidno);
+ rpl_gno get_automatic_gno(rpl_sidno sidno);
+ void lock_sidno(rpl_sidno sidno) { sid_locks.lock(sidno); }
+ void unlock_sidno(rpl_sidno sidno) { sid_locks.unlock(sidno); }
+ void broadcast_sidno(rpl_sidno sidno) { sid_locks.broadcast(sidno); }
+ void wait_for_sidno(rpl_sidno sidno) { sid_locks.wait(sidno); }
+ void lock_sidnos(MY_BITMAP *set);
+ void unlock_sidnos(MY_BITMAP *set);
+ void broadcast_sidnos(MY_BITMAP *set);
private:
- mysql_rwlock_t main_lock;
+ bool contains_group(rpl_sidno sidno, rpl_gno gno) const;
+ //bool contains_sidno(rpl_sidno sidno) const { return used_sidnos
+ Mutex_cond_array sid_locks;
Group_set ended_groups;
Owned_groups owned_groups;
};
@@ -660,7 +823,7 @@ public:
enum enum_subgroup_type
{
- NORMAL_SUBGROUP, ANONYMOUS_SUBGROUP, DUMMY_SUBGROUP
+ NORMAL_SUBGROUP, ANONYMOUS_SUBGROUP, DUMMY_SUBGROUP, AUTOMATIC_SUBGROUP
};
/**
Holds information about a sub-group.
@@ -685,13 +848,15 @@ class Subgroup
/**
Represents a sub-group in the group cache.
- Groups in the group cache are slightly different from other sub-groups
+ Groups in the group cache are slightly different from other
+ sub-groups, because not all information about them is known.
*/
class Cached_subgroup
{
+public:
enum_subgroup_type type;
- rpl_gno gno;
rpl_sidno sidno;
+ rpl_gno gno;
rpl_binlog_pos binlog_pos;
rpl_binlog_pos binlog_length;
rpl_binlog_pos binlog_offset_after_last_statement;
@@ -700,12 +865,78 @@ class Cached_subgroup
};
/**
- Represents a group cache: either the statement group cache or the
- transaction group cache.
+ Represents a group cache: either the statement group cache or the
+ transaction group cache.
*/
class Group_cache
{
+public:
+ /**
+ Constructs a new Group_cache.
+
+ @param default_max_sidno The maximal SIDNO to allocate space for initially.
+ @param error Will be set to GS_SUCCESS or GS_ERROR_OUT_OF_MEMORY.
+ */
+ Group_cache(rpl_sidno default_max_sidno, enum_group_status *error);
+ /**
+ Appends the given sub-group to this cache.
+ */
+ enum_group_status append(Cached_subgroup *subgroup);
+ /**
+ Removes all sub-groups from this cache.
+ */
+ void clear(rpl_sidno max_sidno);
+ /**
+ Return the number of sub-groups in this group cache.
+ */
+ int get_n_subgroups() { return groups.elements; }
+ /**
+ If the cache does not contain the given group, and the group is
+ unlogged, add a dummy group to the cache.
+
+ @param gls Group_log_state, used to determine if the group is
+ unlogged or not.
+ @param sidno SIDNO of the group.
+ @param gno GNO of the group.
+ @return GS_SUCCESS or GS_OUT_OF_MEMORY.
+ */
+ enum_group_status
+ add_dummy_group_for_missing_group(Group_log_state *gls,
+ rpl_sidno sidno, rpl_gno gno);
+ /**
+ For each group in the given Group_set, if the cache does not
+ contain the group, and the group is unlogged, add a dummy group to
+ the cache.
+
+ @param gls Group_log_state, used to determine if the group is
+ unlogged or not.
+ @param group_set The set of groups to possibly add.
+ @return GS_SUCCESS or GS_OUT_OF_MEMORY.
+ */
+ enum_group_status
+ add_dummy_groups_for_missing_groups(Group_log_state *gls,
+ Group_set *group_set);
+ /**
+ Writes all sub-groups in the cache to the group log.
+
+ @todo The group log is not yet implemented /Sven
+ */
+ //enum_group_status write_to_log();
private:
+ enum_group_status allocate_bitmap(rpl_sidno max_sidno);
+ /**
+ Returns a pointer to the given subgroup. The pointer is only
+ valid until the next call to append().
+
+ @param index Index of the element: 0 <= index < get_n_elements().
+ */
+ Cached_subgroup *get_unsafe_pointer(int index)
+ {
+ DBUG_ASSERT(index >= 0 && index < get_n_subgroups());
+ return dynamic_element(&groups, index, Cached_subgroup *);
+ }
+ bool contains_group(rpl_sidno sidno, rpl_gno gno) const;
+ MY_BITMAP used_sidnos;
DYNAMIC_ARRAY groups;
};
=== modified file 'sql/zowned_groups.cc'
--- a/sql/zowned_groups.cc 2011-04-28 16:16:51 +0000
+++ b/sql/zowned_groups.cc 2011-05-03 17:52:32 +0000
@@ -73,4 +73,22 @@ enum_group_status Owned_groups::set_owne
DBUG_RETURN(GS_SUCCESS);
}
+bool Owned_groups::mark_partial(rpl_sidno sidno, rpl_gno gno)
+{
+ DBUG_ENTER("Owned_groups::mark_partial");
+ Node *n= get_node(sidno, gno);
+ DBUG_ASSERT(n != NULL);
+ bool old_partial= n->is_partial;
+ n->is_partial= true;
+ DBUG_RETURN(old_partial);
+}
+
+bool Owned_groups::is_partial(rpl_sidno sidno, rpl_gno gno) const
+{
+ DBUG_ENTER("Owned_groups::is_partial");
+ Node *n= get_node(sidno, gno);
+ DBUG_ASSERT(n != NULL);
+ DBUG_RETURN(n->is_partial);
+}
+
#endif /* HAVE_UGID */
=== modified file 'sql/zsid_map.cc'
--- a/sql/zsid_map.cc 2011-04-28 16:16:51 +0000
+++ b/sql/zsid_map.cc 2011-05-03 17:52:32 +0000
@@ -53,8 +53,8 @@ rpl_sidno Sid_map::add_permanent(rpl_sid
rpl_sidno sidno= get_max_sidno() + 1;
node->sidno= sidno;
node->sid= *sid;
- mysql_mutex_init(NULL, &node->mutex, 0);
- mysql_cond_init(NULL, &node->cond, 0);
+ //mysql_mutex_init(NULL, &node->mutex, 0);
+ //mysql_cond_init(NULL, &node->cond, 0);
if (insert_dynamic(&_sidno_to_sid, &node) == 0)
{
if (insert_dynamic(&_sorted, &sidno) == 0)
=== modified file 'unittest/gunit/rpl_group_set-t.cc'
--- a/unittest/gunit/rpl_group_set-t.cc 2011-04-26 10:48:53 +0000
+++ b/unittest/gunit/rpl_group_set-t.cc 2011-05-03 17:52:32 +0000
@@ -259,5 +259,5 @@ TEST_F(GroupTest, Group_log_state)
sm.add_permanent(&sids[0]);
sm.add_permanent(&sids[1]);
- gls.end_automatic_group(1);
+ gls.get_automatic_gno(1);
}
Attachment: [text/bzr-bundle] bzr/sven.sandberg@oracle.com-20110503175232-a9n1534kveasz6di.bundle
| Thread |
|---|
| • bzr commit into mysql-trunk branch (sven.sandberg:3338) | Sven Sandberg | 3 May |