1
0
mirror of https://github.com/mariadb-corporation/mariadb-columnstore-engine.git synced 2025-07-30 19:23:07 +03:00

Merge pull request #1371 from mariadb-corporation/columnstore_cache

ColumnStore Cache fixes
This commit is contained in:
David.Hall
2020-08-24 15:48:56 -05:00
committed by GitHub
6 changed files with 500 additions and 272 deletions

View File

@ -33,14 +33,6 @@
#define CACHE_PREFIX "#cache#" #define CACHE_PREFIX "#cache#"
static handler* mcs_create_handler(handlerton* hton,
TABLE_SHARE* table,
MEM_ROOT* mem_root);
static int mcs_commit(handlerton* hton, THD* thd, bool all);
static int mcs_rollback(handlerton* hton, THD* thd, bool all);
static int mcs_close_connection(handlerton* hton, THD* thd );
handlerton* mcs_hton = NULL; handlerton* mcs_hton = NULL;
// This is the maria handlerton that we need for the cache // This is the maria handlerton that we need for the cache
static handlerton *mcs_maria_hton = NULL; static handlerton *mcs_maria_hton = NULL;
@ -125,68 +117,6 @@ int mcs_discover_existence(handlerton* hton, const char* db,
return ha_mcs_impl_discover_existence(db, table_name); return ha_mcs_impl_discover_existence(db, table_name);
} }
static int columnstore_init_func(void* p)
{
DBUG_ENTER("columnstore_init_func");
struct tm tm;
time_t t;
time(&t);
localtime_r(&t, &tm);
fprintf(stderr, "%02d%02d%02d %2d:%02d:%02d ",
tm.tm_year % 100, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
fprintf(stderr, "Columnstore: Started; Version: %s-%s\n", columnstore_version.c_str(), columnstore_release.c_str());
strncpy(cs_version, columnstore_version.c_str(), sizeof(cs_version));
cs_version[sizeof(cs_version) - 1] = 0;
strncpy(cs_commit_hash, columnstore_commit_hash.c_str(), sizeof(cs_commit_hash));
cs_commit_hash[sizeof(cs_commit_hash) - 1] = 0;
mcs_hton = (handlerton*)p;
#ifndef _MSC_VER
(void) pthread_mutex_init(&mcs_mutex, MY_MUTEX_INIT_FAST);
#endif
(void) my_hash_init(PSI_NOT_INSTRUMENTED, &mcs_open_tables, system_charset_info, 32, 0, 0,
(my_hash_get_key) mcs_get_key, 0, 0);
mcs_hton->create = mcs_create_handler;
mcs_hton->flags = HTON_CAN_RECREATE;
// mcs_hton->discover_table = mcs_discover;
// mcs_hton->discover_table_existence = mcs_discover_existence;
mcs_hton->commit = mcs_commit;
mcs_hton->rollback = mcs_rollback;
mcs_hton->close_connection = mcs_close_connection;
mcs_hton->create_group_by = create_columnstore_group_by_handler;
mcs_hton->create_derived = create_columnstore_derived_handler;
mcs_hton->create_select = create_columnstore_select_handler;
mcs_hton->db_type = DB_TYPE_AUTOASSIGN;
DBUG_RETURN(0);
}
static int columnstore_done_func(void* p)
{
DBUG_ENTER("columnstore_done_func");
config::Config::deleteInstanceMap();
my_hash_free(&mcs_open_tables);
#ifndef _MSC_VER
pthread_mutex_destroy(&mcs_mutex);
#endif
DBUG_RETURN(0);
}
static handler* mcs_create_handler(handlerton* hton,
TABLE_SHARE* table,
MEM_ROOT* mem_root)
{
return new (mem_root) ha_mcs(hton, table);
}
static int mcs_commit(handlerton* hton, THD* thd, bool all) static int mcs_commit(handlerton* hton, THD* thd, bool all)
{ {
int rc; int rc;
@ -1223,11 +1153,10 @@ bool ha_mcs::is_crashed() const
lock. lock.
*/ */
static my_bool (*original_get_status)(void*, my_bool);
my_bool get_status_and_flush_cache(void *param, my_bool get_status_and_flush_cache(void *param,
my_bool concurrent_insert); my_bool concurrent_insert);
/* /*
Create a name for the cache table Create a name for the cache table
*/ */
@ -1243,7 +1172,9 @@ static void create_cache_name(char *to, const char *name)
THR_LOCK wrapper functions THR_LOCK wrapper functions
The idea of these is to highjack 'THR_LOCK->get_status() so that if this The idea of these is to highjack 'THR_LOCK->get_status() so that if this
is called in a non-insert context then we will flush the cache is called in a non-insert context then we will flush the cache.
We also hijack THR_LOCK->start_trans() to free any locks on the cache
if the command was not an insert command.
*****************************************************************************/ *****************************************************************************/
/* /*
@ -1264,13 +1195,16 @@ my_bool get_status_and_flush_cache(void *param,
Call first the original Aria get_status function Call first the original Aria get_status function
All Aria get_status functions takes Maria handler as the parameter All Aria get_status functions takes Maria handler as the parameter
*/ */
if (original_get_status) if (cache->share->org_lock.get_status)
(*original_get_status)(&cache->cache_handler->file, concurrent_insert); (*cache->share->org_lock.get_status)(cache->cache_handler->file,
concurrent_insert);
/* If first get_status() call for this table, flush cache if needed */ /* If first get_status() call for this table, flush cache if needed */
if (!cache->lock_counter++) if (!cache->lock_counter++)
{ {
if (!cache->insert_command && cache->rows_cached()) ha_rows num_rows = cache->num_rows_cached();
if ((!cache->insert_command && num_rows != 0) ||
num_rows >= get_cache_flush_threshold(current_thd))
{ {
if ((error= cache->flush_insert_cache())) if ((error= cache->flush_insert_cache()))
{ {
@ -1280,71 +1214,165 @@ my_bool get_status_and_flush_cache(void *param,
return(1); return(1);
} }
} }
else if (!cache->insert_command)
cache->free_locks();
} }
else if (!cache->insert_command)
cache->free_locks();
return (0); return (0);
} }
/* Pass through functions for all the THR_LOCK virtual functions */ /*
start_trans() is called when all locks has been given
If this was not an insert command then we can free the write lock on
the cache table and also downgrade external lock for the cached table
to F_READ
*/
static my_bool cache_start_trans(void* param) my_bool cache_start_trans(void* param)
{ {
ha_mcs_cache *cache= (ha_mcs_cache*) param; ha_mcs_cache *cache= (ha_mcs_cache*) param;
if (cache->org_lock.start_trans)
return (*cache->org_lock.start_trans)(cache->cache_handler->file); if (!cache->insert_command)
return 0; {
cache->free_locks();
return 0;
}
return (*cache->share->org_lock.start_trans)(cache->cache_handler->file);
} }
/* Pass through functions for all the THR_LOCK virtual functions */
static void cache_copy_status(void* to, void *from) static void cache_copy_status(void* to, void *from)
{ {
ha_mcs_cache *to_cache= (ha_mcs_cache*) to, *from_cache= (ha_mcs_cache*) from; ha_mcs_cache *to_cache= (ha_mcs_cache*) to, *from_cache= (ha_mcs_cache*) from;
if (to_cache->org_lock.copy_status) (*to_cache->share->org_lock.copy_status)(to_cache->cache_handler->file,
(*to_cache->org_lock.copy_status)(to_cache->cache_handler->file, from_cache->cache_handler->file);
from_cache->cache_handler->file);
} }
static void cache_update_status(void* param) static void cache_update_status(void* param)
{ {
ha_mcs_cache *cache= (ha_mcs_cache*) param; ha_mcs_cache *cache= (ha_mcs_cache*) param;
if (cache->org_lock.update_status) (*cache->share->org_lock.update_status)(cache->cache_handler->file);
(*cache->org_lock.update_status)(cache->cache_handler->file);
} }
static void cache_restore_status(void *param) static void cache_restore_status(void *param)
{ {
ha_mcs_cache *cache= (ha_mcs_cache*) param; ha_mcs_cache *cache= (ha_mcs_cache*) param;
if (cache->org_lock.restore_status) (*cache->share->org_lock.restore_status)(cache->cache_handler->file);
(*cache->org_lock.restore_status)(cache->cache_handler->file);
} }
static my_bool cache_check_status(void *param) static my_bool cache_check_status(void *param)
{ {
ha_mcs_cache *cache= (ha_mcs_cache*) param; ha_mcs_cache *cache= (ha_mcs_cache*) param;
if (cache->org_lock.check_status) return (*cache->share->org_lock.check_status)(cache->cache_handler->file);
return (*cache->org_lock.check_status)(cache->cache_handler->file);
return 0;
} }
/*****************************************************************************
ha_mcs_cache_share functions (Common storage for an open cache file)
*****************************************************************************/
static ha_mcs_cache_share *cache_share_list= 0;
static PSI_mutex_key key_LOCK_cache_share;
static PSI_mutex_info all_mutexes[]=
{
{ &key_LOCK_cache_share, "LOCK_cache_share", PSI_FLAG_GLOBAL},
};
static mysql_mutex_t LOCK_cache_share;
/*
Find or create a share
*/
ha_mcs_cache_share *find_cache_share(const char *name, ulonglong cached_rows)
{
ha_mcs_cache_share *pos, *share;
mysql_mutex_lock(&LOCK_cache_share);
for (pos= cache_share_list; pos; pos= pos->next)
{
if (!strcmp(pos->name, name))
{
pos->open_count++;
mysql_mutex_unlock(&LOCK_cache_share);
return(pos);
}
}
if (!(share= (ha_mcs_cache_share*) my_malloc(PSI_NOT_INSTRUMENTED,
sizeof(*share) + strlen(name)+1,
MYF(MY_FAE))))
{
mysql_mutex_unlock(&LOCK_cache_share);
return 0;
}
share->name= (char*) (share+1);
share->open_count= 1;
share->cached_rows= cached_rows;
strmov((char*) share->name, name);
share->next= cache_share_list;
cache_share_list= share;
mysql_mutex_unlock(&LOCK_cache_share);
return share;
}
/*
Decrement open counter and free share if there is no more users
*/
void ha_mcs_cache_share::close()
{
ha_mcs_cache_share *pos;
mysql_mutex_lock(&LOCK_cache_share);
if (!--open_count)
{
ha_mcs_cache_share **prev= &cache_share_list;
for ( ; (pos= *prev) != this; prev= &pos->next)
;
*prev= next;
my_free(this);
}
mysql_mutex_unlock(&LOCK_cache_share);
}
/***************************************************************************** /*****************************************************************************
ha_mcs_cache handler functions ha_mcs_cache handler functions
*****************************************************************************/ *****************************************************************************/
static plugin_ref plugin_maria = NULL;
ha_mcs_cache::ha_mcs_cache(handlerton *hton, TABLE_SHARE *table_arg, MEM_ROOT *mem_root) ha_mcs_cache::ha_mcs_cache(handlerton *hton, TABLE_SHARE *table_arg, MEM_ROOT *mem_root)
:ha_mcs(mcs_hton, table_arg) :ha_mcs(mcs_hton, table_arg)
{ {
cache_handler= (ha_maria*) mcs_maria_hton->create(mcs_maria_hton, table_arg, mem_root); if (get_cache_inserts(current_thd))
lock_counter= 0; {
if (!plugin_maria)
{
LEX_CSTRING name = { STRING_WITH_LEN("Aria") };
plugin_maria = ha_resolve_by_name(0, &name, 0);
mcs_maria_hton = plugin_hton(plugin_maria);
int error = mcs_maria_hton == NULL; // Engine must exists!
if (error)
my_error(HA_ERR_INITIALIZATION, MYF(0),
"Could not find storage engine %s", name.str);
}
assert(mcs_maria_hton);
cache_handler= (ha_maria*) mcs_maria_hton->create(mcs_maria_hton, table_arg, mem_root);
share= 0;
lock_counter= 0;
cache_locked= 0;
}
} }
ha_mcs_cache::~ha_mcs_cache() ha_mcs_cache::~ha_mcs_cache()
{ {
if (cache_handler) if (get_cache_inserts(current_thd) && cache_handler)
{
delete cache_handler; delete cache_handler;
cache_handler= NULL;
}
} }
/* /*
@ -1359,26 +1387,31 @@ int ha_mcs_cache::create(const char *name, TABLE *table_arg,
char cache_name[FN_REFLEN+8]; char cache_name[FN_REFLEN+8];
DBUG_ENTER("ha_mcs_cache::create"); DBUG_ENTER("ha_mcs_cache::create");
create_cache_name(cache_name, name); if (get_cache_inserts(current_thd))
{ {
/* Create a cached table */ create_cache_name(cache_name, name);
ha_choice save_transactional= ha_create_info->transactional; {
row_type save_row_type= ha_create_info->row_type; /* Create a cached table */
ha_create_info->transactional= HA_CHOICE_NO; ha_choice save_transactional= ha_create_info->transactional;
ha_create_info->row_type= ROW_TYPE_DYNAMIC; row_type save_row_type= ha_create_info->row_type;
ha_create_info->transactional= HA_CHOICE_NO;
ha_create_info->row_type= ROW_TYPE_DYNAMIC;
if ((error= cache_handler->create(cache_name, table_arg, ha_create_info))) if ((error= cache_handler->create(cache_name, table_arg, ha_create_info)))
DBUG_RETURN(error); DBUG_RETURN(error);
ha_create_info->transactional= save_transactional; ha_create_info->transactional= save_transactional;
ha_create_info->row_type= save_row_type; ha_create_info->row_type= save_row_type;
}
} }
/* Create the real table in ColumnStore */ /* Create the real table in ColumnStore */
if ((error= parent::create(name, table_arg, ha_create_info))) if ((error= parent::create(name, table_arg, ha_create_info)))
{ {
cache_handler->delete_table(cache_name); if (get_cache_inserts(current_thd))
cache_handler->delete_table(cache_name);
DBUG_RETURN(error); DBUG_RETURN(error);
} }
DBUG_RETURN(0); DBUG_RETURN(0);
} }
@ -1386,35 +1419,64 @@ int ha_mcs_cache::create(const char *name, TABLE *table_arg,
int ha_mcs_cache::open(const char *name, int mode, uint open_flags) int ha_mcs_cache::open(const char *name, int mode, uint open_flags)
{ {
int error; int error;
char cache_name[FN_REFLEN+8];
DBUG_ENTER("ha_mcs_cache::open"); DBUG_ENTER("ha_mcs_cache::open");
/* Copy table object to cache_handler */ if (get_cache_inserts(current_thd))
cache_handler->change_table_ptr(table, table->s); {
/* Copy table object to cache_handler */
cache_handler->change_table_ptr(table, table->s);
create_cache_name(cache_name, name); char cache_name[FN_REFLEN+8];
if ((error= cache_handler->open(cache_name, mode, open_flags))) create_cache_name(cache_name, name);
DBUG_RETURN(error); if ((error= cache_handler->open(cache_name, mode, open_flags)))
DBUG_RETURN(error);
/* Fix lock so that it goes through get_status_and_flush() */ if (!(share= find_cache_share(name, cache_handler->file->state->records)))
THR_LOCK *lock= &cache_handler->file->s->lock; {
mysql_mutex_lock(&cache_handler->file->s->intern_lock); cache_handler->close();
org_lock= lock[0]; DBUG_RETURN(ER_OUTOFMEMORY);
lock->get_status= &get_status_and_flush_cache; }
lock->start_trans= &cache_start_trans;
lock->copy_status= &cache_copy_status; /* Fix lock so that it goes through get_status_and_flush() */
lock->update_status= &cache_update_status; THR_LOCK *lock= &cache_handler->file->s->lock;
lock->restore_status= &cache_restore_status; if (lock->get_status != &get_status_and_flush_cache)
lock->check_status= &cache_check_status; {
lock->restore_status= &cache_restore_status; mysql_mutex_lock(&cache_handler->file->s->intern_lock);
cache_handler->file->lock.status_param= (void*) this;
mysql_mutex_unlock(&cache_handler->file->s->intern_lock); /* The following lock is here just to establish mutex locking order */
mysql_mutex_lock(&lock->mutex);
mysql_mutex_unlock(&lock->mutex);
if (lock->get_status != &get_status_and_flush_cache)
{
/* Remember original lock. Used by the THR_lock cache functions */
share->org_lock= lock[0];
if (lock->start_trans)
lock->start_trans= &cache_start_trans;
if (lock->copy_status)
lock->copy_status= &cache_copy_status;
if (lock->update_status)
lock->update_status= &cache_update_status;
if (lock->restore_status)
lock->restore_status= &cache_restore_status;
if (lock->check_status)
lock->check_status= &cache_check_status;
if (lock->restore_status)
lock->restore_status= &cache_restore_status;
lock->get_status= &get_status_and_flush_cache;
}
mysql_mutex_unlock(&cache_handler->file->s->intern_lock);
}
cache_handler->file->lock.status_param= (void*) this;
}
if ((error= parent::open(name, mode, open_flags))) if ((error= parent::open(name, mode, open_flags)))
{ {
cache_handler->close(); if (get_cache_inserts(current_thd))
cache_handler->close();
DBUG_RETURN(error); DBUG_RETURN(error);
} }
DBUG_RETURN(0); DBUG_RETURN(0);
} }
@ -1422,10 +1484,23 @@ int ha_mcs_cache::open(const char *name, int mode, uint open_flags)
int ha_mcs_cache::close() int ha_mcs_cache::close()
{ {
int error, error2; int error, error2;
DBUG_ENTER("ha_mcs_cache::close()"); DBUG_ENTER("ha_mcs_cache::close()");
error= cache_handler->close();
if ((error2= parent::close())) if (get_cache_inserts(current_thd))
error= error2; {
error= cache_handler->close();
if ((error2= parent::close()))
error= error2;
ha_mcs_cache_share *org_share= share;
if (org_share)
org_share->close();
}
else
{
error= parent::close();
}
DBUG_RETURN(error); DBUG_RETURN(error);
} }
@ -1439,9 +1514,12 @@ uint ha_mcs_cache::lock_count(void) const
{ {
/* /*
If we are doing an insert or if we want to flush the cache, we have to lock If we are doing an insert or if we want to flush the cache, we have to lock
both MyISAM table and normal table. both the Aria table and normal table.
*/ */
return 2; if (get_cache_inserts(current_thd))
return 2;
else
return 1;
} }
/** /**
@ -1452,7 +1530,8 @@ THR_LOCK_DATA **ha_mcs_cache::store_lock(THD *thd,
THR_LOCK_DATA **to, THR_LOCK_DATA **to,
enum thr_lock_type lock_type) enum thr_lock_type lock_type)
{ {
to= cache_handler->store_lock(thd, to, TL_WRITE); if (get_cache_inserts(current_thd))
to= cache_handler->store_lock(thd, to, TL_WRITE);
return parent::store_lock(thd, to, lock_type); return parent::store_lock(thd, to, lock_type);
} }
@ -1463,78 +1542,113 @@ THR_LOCK_DATA **ha_mcs_cache::store_lock(THD *thd,
int ha_mcs_cache::external_lock(THD *thd, int lock_type) int ha_mcs_cache::external_lock(THD *thd, int lock_type)
{ {
int error; int error= 0;
DBUG_ENTER("ha_mcs_cache::external_lock"); DBUG_ENTER("ha_mcs_cache::external_lock");
/* if (get_cache_inserts(current_thd))
Reset lock_counter. This is ok as external_lock() is guaranteed to be
called before first get_status()
*/
lock_counter= 0;
if (lock_type == F_UNLCK)
{ {
int error2; /*
error= cache_handler->external_lock(thd, lock_type); Reset lock_counter. This is ok as external_lock() is guaranteed to be
if ((error2= parent::external_lock(thd, lock_type))) called before first get_status()
error= error2; */
DBUG_RETURN(error); lock_counter= 0;
if (lock_type == F_UNLCK)
{
int error2;
error= 0;
if (cache_locked)
{
error= cache_handler->external_lock(thd, lock_type);
cache_locked= 0;
}
if ((error2= parent::external_lock(thd, lock_type)))
error= error2;
DBUG_RETURN(error);
}
/* Lock first with write lock to be able to do insert or flush table */
original_lock_type= lock_type;
lock_type= F_WRLCK;
if ((error= cache_handler->external_lock(thd, lock_type)))
DBUG_RETURN(error);
if ((error= parent::external_lock(thd, lock_type)))
{
error= cache_handler->external_lock(thd, F_UNLCK);
DBUG_RETURN(error);
}
cache_locked= 1;
}
else
{
error= parent::external_lock(thd, lock_type);
} }
/* Lock first with write lock to be able to do insert or flush table */ DBUG_RETURN(error);
original_lock_type= lock_type;
lock_type= F_WRLCK;
if ((error= cache_handler->external_lock(thd, lock_type)))
DBUG_RETURN(error);
if ((error= parent::external_lock(thd, lock_type)))
{
error= cache_handler->external_lock(thd, F_UNLCK);
DBUG_RETURN(error);
}
DBUG_RETURN(0);
} }
int ha_mcs_cache::delete_table(const char *name) int ha_mcs_cache::delete_table(const char *name)
{ {
int error, error2; int error= 0, error2;
char cache_name[FN_REFLEN+8];
DBUG_ENTER("ha_mcs_cache::delete_table"); DBUG_ENTER("ha_mcs_cache::delete_table");
create_cache_name(cache_name, name); if (get_cache_inserts(current_thd))
error= cache_handler->delete_table(cache_name); {
char cache_name[FN_REFLEN+8];
create_cache_name(cache_name, name);
error= cache_handler->delete_table(cache_name);
}
if ((error2= parent::delete_table(name))) if ((error2= parent::delete_table(name)))
error= error2; error= error2;
DBUG_RETURN(error); DBUG_RETURN(error);
} }
int ha_mcs_cache::rename_table(const char *from, const char *to) int ha_mcs_cache::rename_table(const char *from, const char *to)
{ {
int error; int error= 0;
char cache_from[FN_REFLEN+8], cache_to[FN_REFLEN+8];
DBUG_ENTER("ha_mcs_cache::rename_table"); DBUG_ENTER("ha_mcs_cache::rename_table");
create_cache_name(cache_from, from); if (get_cache_inserts(current_thd))
create_cache_name(cache_to, to);
if ((error= cache_handler->rename_table(cache_from, cache_to)))
DBUG_RETURN(error);
if ((error= parent::rename_table(from, to)))
{ {
cache_handler->rename_table(cache_to, cache_from); char cache_from[FN_REFLEN+8], cache_to[FN_REFLEN+8];
DBUG_RETURN(error); create_cache_name(cache_from, from);
create_cache_name(cache_to, to);
if ((error= cache_handler->rename_table(cache_from, cache_to)))
DBUG_RETURN(error);
if ((error= parent::rename_table(from, to)))
{
cache_handler->rename_table(cache_to, cache_from);
DBUG_RETURN(error);
}
} }
DBUG_RETURN(0); else
{
error= parent::rename_table(from, to);
}
DBUG_RETURN(error);
} }
int ha_mcs_cache::delete_all_rows(void) int ha_mcs_cache::delete_all_rows(void)
{ {
int error,error2; int error= 0, error2;
DBUG_ENTER("ha_mcs_cache::delete_all_rows"); DBUG_ENTER("ha_mcs_cache::delete_all_rows");
error= cache_handler->delete_all_rows(); if (get_cache_inserts(current_thd))
{
error= cache_handler->delete_all_rows();
share->cached_rows= 0;
}
if ((error2= parent::delete_all_rows())) if ((error2= parent::delete_all_rows()))
error= error2; error= error2;
DBUG_RETURN(error); DBUG_RETURN(error);
@ -1542,8 +1656,11 @@ int ha_mcs_cache::delete_all_rows(void)
bool ha_mcs_cache::is_crashed() const bool ha_mcs_cache::is_crashed() const
{ {
return (cache_handler->is_crashed() || if (get_cache_inserts(current_thd))
parent::is_crashed()); return (cache_handler->is_crashed() ||
parent::is_crashed());
else
return parent::is_crashed();
} }
/** /**
@ -1555,7 +1672,7 @@ bool ha_mcs_cache::is_crashed() const
In the case of 1) we don't want to run repair on both tables as In the case of 1) we don't want to run repair on both tables as
the repair can be a slow process. Instead we only run repair the repair can be a slow process. Instead we only run repair
on the crashed tables. If not tables are marked crashed, we on the crashed tables. If no tables are marked crashed, we
run repair on both tables. run repair on both tables.
Repair on the cache table will delete the part of the cache that was Repair on the cache table will delete the part of the cache that was
@ -1571,17 +1688,23 @@ int ha_mcs_cache::repair(THD *thd, HA_CHECK_OPT *check_opt)
int something_crashed= is_crashed(); int something_crashed= is_crashed();
DBUG_ENTER("ha_mcs_cache::repair"); DBUG_ENTER("ha_mcs_cache::repair");
if (cache_handler->is_crashed() || !something_crashed) if (get_cache_inserts(current_thd))
{ {
/* Delete everything that was not already committed */ if (cache_handler->is_crashed() || !something_crashed)
mysql_file_chsize(cache_handler->file->dfile.file, {
cache_handler->file->s->state.state.key_file_length, /* Delete everything that was not already committed */
0, MYF(MY_WME)); mysql_file_chsize(cache_handler->file->dfile.file,
mysql_file_chsize(cache_handler->file->s->kfile.file, cache_handler->file->s->state.state.data_file_length,
cache_handler->file->s->state.state.data_file_length, 0, MYF(MY_WME));
0, MYF(MY_WME)); mysql_file_chsize(cache_handler->file->s->kfile.file,
error= cache_handler->repair(thd, check_opt); cache_handler->file->s->state.state.key_file_length,
0, MYF(MY_WME));
check_opt->flags|= T_AUTO_REPAIR;
error= cache_handler->repair(thd, check_opt);
share->cached_rows= cache_handler->file->state->records;
}
} }
if (parent::is_crashed() || !something_crashed) if (parent::is_crashed() || !something_crashed)
if ((error2= parent::repair(thd, check_opt))) if ((error2= parent::repair(thd, check_opt)))
error= error2; error= error2;
@ -1595,26 +1718,37 @@ int ha_mcs_cache::repair(THD *thd, HA_CHECK_OPT *check_opt)
*/ */
int ha_mcs_cache::write_row(const uchar *buf) int ha_mcs_cache::write_row(const uchar *buf)
{ {
if (insert_command) if (get_cache_inserts(current_thd) && insert_command)
{
DBUG_ASSERT(share->cached_rows == cache_handler->file->state->records);
share->cached_rows++;
return cache_handler->write_row(buf); return cache_handler->write_row(buf);
}
return parent::write_row(buf); return parent::write_row(buf);
} }
void ha_mcs_cache::start_bulk_insert(ha_rows rows, uint flags) void ha_mcs_cache::start_bulk_insert(ha_rows rows, uint flags)
{ {
if (insert_command) if (get_cache_inserts(current_thd))
{ {
bzero(&cache_handler->copy_info, sizeof(cache_handler->copy_info)); if (insert_command)
return cache_handler->start_bulk_insert(rows, flags); {
bzero(&cache_handler->copy_info, sizeof(cache_handler->copy_info));
return cache_handler->start_bulk_insert(rows, flags);
}
return parent::start_bulk_insert_from_cache(rows, flags);
}
else
{
return parent::start_bulk_insert(rows, flags);
} }
return parent::start_bulk_insert(rows, flags);
} }
int ha_mcs_cache::end_bulk_insert() int ha_mcs_cache::end_bulk_insert()
{ {
if (insert_command) if (get_cache_inserts(current_thd) && insert_command)
return cache_handler->end_bulk_insert(); return cache_handler->end_bulk_insert();
return parent::end_bulk_insert(); return parent::end_bulk_insert();
} }
@ -1623,7 +1757,6 @@ int ha_mcs_cache::end_bulk_insert()
ha_mcs_cache Plugin code ha_mcs_cache Plugin code
******************************************************************************/ ******************************************************************************/
#if 0
static handler *ha_mcs_cache_create_handler(handlerton *hton, static handler *ha_mcs_cache_create_handler(handlerton *hton,
TABLE_SHARE *table, TABLE_SHARE *table,
MEM_ROOT *mem_root) MEM_ROOT *mem_root)
@ -1631,54 +1764,82 @@ static handler *ha_mcs_cache_create_handler(handlerton *hton,
return new (mem_root) ha_mcs_cache(hton, table, mem_root); return new (mem_root) ha_mcs_cache(hton, table, mem_root);
} }
static plugin_ref plugin_maria;
static int ha_mcs_cache_init(void *p) /******************************************************************************
ha_mcs Plugin code
******************************************************************************/
static int columnstore_init_func(void* p)
{ {
handlerton *cache_hton; DBUG_ENTER("columnstore_init_func");
int error;
cache_hton= (handlerton *) p; struct tm tm;
cache_hton->create= ha_mcs_cache_create_handler; time_t t;
cache_hton->panic= 0; time(&t);
cache_hton->flags= HTON_NO_PARTITION; localtime_r(&t, &tm);
fprintf(stderr, "%02d%02d%02d %2d:%02d:%02d ",
tm.tm_year % 100, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
error= mcs_hton == NULL; // Engine must exists! fprintf(stderr, "Columnstore: Started; Version: %s-%s\n", columnstore_version.c_str(), columnstore_release.c_str());
if (error) strncpy(cs_version, columnstore_version.c_str(), sizeof(cs_version));
{ cs_version[sizeof(cs_version) - 1] = 0;
my_error(HA_ERR_INITIALIZATION, MYF(0),
"Could not find storage engine %s", "Columnstore");
return error;
}
{ strncpy(cs_commit_hash, columnstore_commit_hash.c_str(), sizeof(cs_commit_hash));
LEX_CSTRING name= { STRING_WITH_LEN("Aria") }; cs_commit_hash[sizeof(cs_commit_hash) - 1] = 0;
plugin_maria= ha_resolve_by_name(0, &name, 0);
mcs_maria_hton= plugin_hton(plugin_maria);
error= mcs_maria_hton == NULL; // Engine must exists!
if (error)
my_error(HA_ERR_INITIALIZATION, MYF(0),
"Could not find storage engine %s", name.str);
}
return error; mcs_hton = (handlerton*)p;
#ifndef _MSC_VER
(void) pthread_mutex_init(&mcs_mutex, MY_MUTEX_INIT_FAST);
#endif
(void) my_hash_init(PSI_NOT_INSTRUMENTED, &mcs_open_tables, system_charset_info, 32, 0, 0,
(my_hash_get_key) mcs_get_key, 0, 0);
mcs_hton->create = ha_mcs_cache_create_handler;
mcs_hton->panic = 0;
mcs_hton->flags = HTON_CAN_RECREATE | HTON_NO_PARTITION;
// mcs_hton->discover_table = mcs_discover;
// mcs_hton->discover_table_existence = mcs_discover_existence;
mcs_hton->commit = mcs_commit;
mcs_hton->rollback = mcs_rollback;
mcs_hton->close_connection = mcs_close_connection;
mcs_hton->create_group_by = create_columnstore_group_by_handler;
mcs_hton->create_derived = create_columnstore_derived_handler;
mcs_hton->create_select = create_columnstore_select_handler;
mcs_hton->db_type = DB_TYPE_AUTOASSIGN;
uint count = sizeof(all_mutexes)/sizeof(all_mutexes[0]);
mysql_mutex_register("ha_mcs_cache", all_mutexes, count);
mysql_mutex_init(key_LOCK_cache_share, &LOCK_cache_share, MY_MUTEX_INIT_FAST);
DBUG_RETURN(0);
} }
static int ha_mcs_cache_deinit(void *p) static int columnstore_done_func(void* p)
{ {
if (plugin_maria) DBUG_ENTER("columnstore_done_func");
{
plugin_unlock(0, plugin_maria);
plugin_maria= NULL;
}
return 0;
}
config::Config::deleteInstanceMap();
my_hash_free(&mcs_open_tables);
#ifndef _MSC_VER
pthread_mutex_destroy(&mcs_mutex);
#endif
if (plugin_maria)
{
plugin_unlock(0, plugin_maria);
plugin_maria = NULL;
}
mysql_mutex_destroy(&LOCK_cache_share);
DBUG_RETURN(0);
}
struct st_mysql_storage_engine ha_mcs_cache_storage_engine= struct st_mysql_storage_engine ha_mcs_cache_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION }; { MYSQL_HANDLERTON_INTERFACE_VERSION };
#endif
struct st_mysql_storage_engine columnstore_storage_engine = struct st_mysql_storage_engine columnstore_storage_engine =
{ MYSQL_HANDLERTON_INTERFACE_VERSION }; { MYSQL_HANDLERTON_INTERFACE_VERSION };
@ -1703,23 +1864,6 @@ maria_declare_plugin(columnstore)
MCSVERSION, /* string version */ MCSVERSION, /* string version */
COLUMNSTORE_MATURITY /* maturity */ COLUMNSTORE_MATURITY /* maturity */
}, },
#if 0
{
MYSQL_STORAGE_ENGINE_PLUGIN,
&ha_mcs_cache_storage_engine,
"Columnstore_cache",
"MariaDB Corporation AB",
"Insert cache for ColumnStore",
PLUGIN_LICENSE_GPL,
ha_mcs_cache_init, /* Plugin Init */
ha_mcs_cache_deinit, /* Plugin Deinit */
MCSVERSIONHEX,
NULL, /* status variables */
NULL, /* system variables */
MCSVERSION, /* string version */
MariaDB_PLUGIN_MATURITY_ALPHA /* maturity */
},
#endif
{ {
MYSQL_INFORMATION_SCHEMA_PLUGIN, MYSQL_INFORMATION_SCHEMA_PLUGIN,
&is_columnstore_plugin_version, &is_columnstore_plugin_version,
@ -1790,9 +1934,9 @@ maria_declare_plugin_end;
Implementation of write cache Implementation of write cache
******************************************************************************/ ******************************************************************************/
bool ha_mcs_cache::rows_cached() ha_rows ha_mcs_cache::num_rows_cached()
{ {
return cache_handler->file->state->records != 0; return cache_handler->file->state->records;
} }
@ -1800,20 +1944,20 @@ bool ha_mcs_cache::rows_cached()
void ha_mcs_cache::free_locks() void ha_mcs_cache::free_locks()
{ {
/* We don't need to lock cache_handler anymore as it's already flushed */
mysql_mutex_unlock(&cache_handler->file->lock.lock->mutex);
thr_unlock(&cache_handler->file->lock, 0);
mysql_mutex_lock(&cache_handler->file->lock.lock->mutex);
/* Restart transaction for columnstore table */ /* Restart transaction for columnstore table */
if (original_lock_type != F_WRLCK) if (original_lock_type != F_WRLCK)
{ {
parent::external_lock(table->in_use, F_UNLCK); parent::external_lock(table->in_use, F_UNLCK);
parent::external_lock(table->in_use, original_lock_type); parent::external_lock(table->in_use, original_lock_type);
} }
/* We don't need to lock cache_handler anymore as it's already flushed */
cache_handler->external_lock(table->in_use, F_UNLCK);
thr_unlock(&cache_handler->file->lock, 0);
cache_locked= false;
} }
/** /**
Copy data from cache to ColumnStore Copy data from cache to ColumnStore
@ -1826,18 +1970,23 @@ int ha_mcs_cache::flush_insert_cache()
int error, error2; int error, error2;
ha_maria *from= cache_handler; ha_maria *from= cache_handler;
uchar *record= table->record[0]; uchar *record= table->record[0];
ulonglong copied_rows= 0;
DBUG_ENTER("flush_insert_cache"); DBUG_ENTER("flush_insert_cache");
DBUG_ASSERT(from->file->state->records == share->cached_rows);
parent::start_bulk_insert_from_cache(from->file->state->records, 0); parent::start_bulk_insert_from_cache(from->file->state->records, 0);
from->rnd_init(1); from->rnd_init(1);
while (!(error= from->rnd_next(record))) while (!(error= from->rnd_next(record)))
{ {
copied_rows++;
if ((error= parent::write_row(record))) if ((error= parent::write_row(record)))
goto end; goto end;
rows_changed++; rows_changed++;
} }
if (error == HA_ERR_END_OF_FILE) if (error == HA_ERR_END_OF_FILE)
error= 0; error= 0;
DBUG_ASSERT(copied_rows == share->cached_rows);
end: end:
from->rnd_end(); from->rnd_end();
@ -1856,19 +2005,24 @@ end:
parent::ht->rollback(parent::ht, table->in_use, 1); parent::ht->rollback(parent::ht, table->in_use, 1);
} }
DBUG_ASSERT(error == 0);
if (!error) if (!error)
{ {
/* /*
Everything when fine, delete all rows from the cache and allow others Everything went fine, delete all rows from the cache and allow others
to use it. to use it.
*/ */
from->delete_all_rows(); {
/*
/* We have to unlock the lock mutex as otherwise we get a conflict in
This was not an insert command, so we can delete the thr lock mutex order. This is fine as we have a write lock on the mutex,
(We are not going to use the insert cache for this statement anymore) so we will be able to get it again
*/ */
free_locks(); mysql_mutex_unlock(&from->file->s->lock.mutex);
from->delete_all_rows();
share->cached_rows= 0;
mysql_mutex_lock(&from->file->s->lock.mutex);
}
} }
DBUG_RETURN(error); DBUG_RETURN(error);
} }

View File

@ -242,16 +242,31 @@ public:
}; };
class ha_mcs_cache_share
{
ha_mcs_cache_share *next; /* Next open share */
const char *name;
uint open_count;
public:
ulonglong cached_rows;
THR_LOCK org_lock;
friend ha_mcs_cache_share *find_cache_share(const char *name,
ulonglong cached_rows);
void close();
};
class ha_mcs_cache :public ha_mcs class ha_mcs_cache :public ha_mcs
{ {
typedef ha_mcs parent; typedef ha_mcs parent;
int original_lock_type; int original_lock_type;
bool insert_command; bool insert_command, cache_locked;
public: public:
THR_LOCK org_lock;
uint lock_counter; uint lock_counter;
ha_maria *cache_handler; ha_maria *cache_handler;
ha_mcs_cache_share *share;
ha_mcs_cache(handlerton *hton, TABLE_SHARE *table_arg, MEM_ROOT *mem_root); ha_mcs_cache(handlerton *hton, TABLE_SHARE *table_arg, MEM_ROOT *mem_root);
~ha_mcs_cache(); ~ha_mcs_cache();
@ -287,10 +302,11 @@ public:
/* Cache functions */ /* Cache functions */
void free_locks(); void free_locks();
bool rows_cached(); ha_rows num_rows_cached();
int flush_insert_cache(); int flush_insert_cache();
friend my_bool get_status_and_flush_cache(void *param, friend my_bool get_status_and_flush_cache(void *param,
my_bool concurrent_insert); my_bool concurrent_insert);
friend my_bool cache_start_trans(void *param);
}; };
#endif //HA_MCS_H__ #endif //HA_MCS_H__

View File

@ -77,6 +77,7 @@ using namespace joblist;
namespace namespace
{ {
#define BATCH_INSERT_GROUP_ROWS_FOR_CACHE 100000
uint64_t fBatchInsertGroupRows = 0; // ResourceManager::instance()->getRowsPerBatch(); uint64_t fBatchInsertGroupRows = 0; // ResourceManager::instance()->getRowsPerBatch();
// HDFS is never used nowadays, so don't bother // HDFS is never used nowadays, so don't bother
bool useHdfs = false; // ResourceManager::instance()->useHdfs(); bool useHdfs = false; // ResourceManager::instance()->useHdfs();
@ -594,13 +595,17 @@ int ha_mcs_impl_write_row_(const uchar* buf, TABLE* table, cal_connection_info&
} }
if (fBatchInsertGroupRows == 0) if (fBatchInsertGroupRows == 0)
{
fBatchInsertGroupRows = ResourceManager::instance()->getRowsPerBatch(); fBatchInsertGroupRows = ResourceManager::instance()->getRowsPerBatch();
}
//timer.stop( "buildValueList"); //timer.stop( "buildValueList");
if ( ci.singleInsert // Single insert if ( ci.singleInsert // Single insert
|| (( ci.bulkInsertRows > 0 ) && (( ( ci.rowsHaveInserted + size) >= ci.bulkInsertRows ) || ( size >= fBatchInsertGroupRows )) ) || (( ci.bulkInsertRows > 0 ) && (( ( ci.rowsHaveInserted + size) >= ci.bulkInsertRows )
//Insert with mutilple value case: processed batch by batch. Last batch is sent also. || ( (!ci.isCacheInsert && size >= fBatchInsertGroupRows) || (ci.isCacheInsert && size >= BATCH_INSERT_GROUP_ROWS_FOR_CACHE) )) )
|| (( ci.bulkInsertRows == 0 ) && ( size >= fBatchInsertGroupRows ) ) ) // Load data in file is processed batch by batch //Insert with mutilple value case: processed batch by batch. Last batch is sent also.
|| (( ci.bulkInsertRows == 0 ) && ( (!ci.isCacheInsert && size >= fBatchInsertGroupRows)
|| (ci.isCacheInsert && size >= BATCH_INSERT_GROUP_ROWS_FOR_CACHE) ) ) ) // Load data in file is processed batch by batch
{ {
//timer.start( "DMLProc takes"); //timer.start( "DMLProc takes");
//cout <<" sending a batch to DMLProc ... The size is " << size << " the current bulkInsertRows = " << ci.bulkInsertRows << endl; //cout <<" sending a batch to DMLProc ... The size is " << size << " the current bulkInsertRows = " << ci.bulkInsertRows << endl;

View File

@ -3161,7 +3161,7 @@ void ha_mcs_impl_start_bulk_insert(ha_rows rows, TABLE* table, bool is_cache_ins
ci->isLoaddataInfile = true; ci->isLoaddataInfile = true;
} }
if (is_cache_insert) if (is_cache_insert && (thd->lex)->sql_command != SQLCOM_INSERT_SELECT)
{ {
ci->isCacheInsert = true; ci->isCacheInsert = true;
@ -3174,7 +3174,7 @@ void ha_mcs_impl_start_bulk_insert(ha_rows rows, TABLE* table, bool is_cache_ins
if ((((thd->lex)->sql_command == SQLCOM_INSERT) || if ((((thd->lex)->sql_command == SQLCOM_INSERT) ||
((thd->lex)->sql_command == SQLCOM_LOAD) || ((thd->lex)->sql_command == SQLCOM_LOAD) ||
(thd->lex)->sql_command == SQLCOM_INSERT_SELECT || (thd->lex)->sql_command == SQLCOM_INSERT_SELECT ||
is_cache_insert) && !ci->singleInsert ) ci->isCacheInsert) && !ci->singleInsert )
{ {
ci->useCpimport = get_use_import_for_batchinsert(thd); ci->useCpimport = get_use_import_for_batchinsert(thd);
@ -3182,7 +3182,7 @@ void ha_mcs_impl_start_bulk_insert(ha_rows rows, TABLE* table, bool is_cache_ins
ci->useCpimport = 0; ci->useCpimport = 0;
// For now, disable cpimport for cache inserts // For now, disable cpimport for cache inserts
if (is_cache_insert) if (ci->isCacheInsert)
ci->useCpimport = 0; ci->useCpimport = 0;
// ci->useCpimport = 2 means ALWAYS use cpimport, whether it's in a // ci->useCpimport = 2 means ALWAYS use cpimport, whether it's in a
@ -3215,6 +3215,11 @@ void ha_mcs_impl_start_bulk_insert(ha_rows rows, TABLE* table, bool is_cache_ins
} }
ci->useXbit = table->s->db_options_in_use & HA_OPTION_PACK_RECORD; ci->useXbit = table->s->db_options_in_use & HA_OPTION_PACK_RECORD;
// TODO: This needs a proper fix.
if (is_cache_insert)
ci->useXbit = false;
//@bug 6122 Check how many columns have not null constraint. columnn with not null constraint will not show up in header. //@bug 6122 Check how many columns have not null constraint. columnn with not null constraint will not show up in header.
unsigned int numberNotNull = 0; unsigned int numberNotNull = 0;
@ -3541,7 +3546,7 @@ void ha_mcs_impl_start_bulk_insert(ha_rows rows, TABLE* table, bool is_cache_ins
} }
//Save table oid for commit to use //Save table oid for commit to use
if ( ( ((thd->lex)->sql_command == SQLCOM_INSERT) || ((thd->lex)->sql_command == SQLCOM_LOAD) || (thd->lex)->sql_command == SQLCOM_INSERT_SELECT) || is_cache_insert) if ( ( ((thd->lex)->sql_command == SQLCOM_INSERT) || ((thd->lex)->sql_command == SQLCOM_LOAD) || (thd->lex)->sql_command == SQLCOM_INSERT_SELECT) || ci->isCacheInsert)
{ {
// query stats. only collect execution time and rows inserted for insert/load_data_infile // query stats. only collect execution time and rows inserted for insert/load_data_infile
ci->stats.reset(); ci->stats.reset();

View File

@ -303,6 +303,27 @@ static MYSQL_THDVAR_BOOL(
0 0
); );
static MYSQL_THDVAR_BOOL(
cache_inserts,
PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY,
"Perform cache-based inserts to ColumnStore",
NULL,
NULL,
0
);
static MYSQL_THDVAR_ULONGLONG(
cache_flush_threshold,
PLUGIN_VAR_RQCMDARG,
"Threshold on the number of rows in the cache to trigger a flush",
NULL,
NULL,
500000,
1,
1000000000,
1
);
st_mysql_sys_var* mcs_system_variables[] = st_mysql_sys_var* mcs_system_variables[] =
{ {
MYSQL_SYSVAR(compression_type), MYSQL_SYSVAR(compression_type),
@ -328,6 +349,8 @@ st_mysql_sys_var* mcs_system_variables[] =
MYSQL_SYSVAR(import_for_batchinsert_enclosed_by), MYSQL_SYSVAR(import_for_batchinsert_enclosed_by),
MYSQL_SYSVAR(varbin_always_hex), MYSQL_SYSVAR(varbin_always_hex),
MYSQL_SYSVAR(replication_slave), MYSQL_SYSVAR(replication_slave),
MYSQL_SYSVAR(cache_inserts),
MYSQL_SYSVAR(cache_flush_threshold),
NULL NULL
}; };
@ -559,3 +582,22 @@ void set_replication_slave(THD* thd, bool value)
{ {
THDVAR(thd, replication_slave) = value; THDVAR(thd, replication_slave) = value;
} }
bool get_cache_inserts(THD* thd)
{
return ( thd == NULL ) ? false : THDVAR(thd, cache_inserts);
}
void set_cache_inserts(THD* thd, bool value)
{
THDVAR(thd, cache_inserts) = value;
}
ulonglong get_cache_flush_threshold(THD* thd)
{
return ( thd == NULL ) ? 500000 : THDVAR(thd, cache_flush_threshold);
}
void set_cache_flush_threshold(THD* thd, ulonglong value)
{
THDVAR(thd, cache_flush_threshold) = value;
}

View File

@ -113,4 +113,10 @@ void set_import_for_batchinsert_enclosed_by(THD* thd, ulong value);
bool get_replication_slave(THD* thd); bool get_replication_slave(THD* thd);
void set_replication_slave(THD* thd, bool value); void set_replication_slave(THD* thd, bool value);
bool get_cache_inserts(THD* thd);
void set_cache_inserts(THD* thd, bool value);
ulonglong get_cache_flush_threshold(THD* thd);
void set_cache_flush_threshold(THD* thd, ulonglong value);
#endif #endif