From: Magne Mahre Date: October 21 2010 10:52am Subject: bzr commit into mysql-next-mr-bugfixing branch (magne.mahre:3328) Bug#49177 List-Archive: http://lists.mysql.com/commits/121507 X-Bug: 49177 Message-Id: <201010211055.o9LAtTKM016210@dm-uk-02.uk.sun.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============1518316327287374743==" --===============1518316327287374743== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///export/home/tmp/x/mysql-next-mr-bugfixing-49177/ based on revid:anitha.gopi@stripped 3328 Magne Mahre 2010-10-21 Bug#49177 MyISAM share list scalability problems (Note: MyISAM issue only) As the number of open tables is increased, table lookup (testing if a table is already open) and (in particular) the case when a table is not open, became increasingly more expensive. The problem was caused by the open table lookup mechanism, which was based on traversing a linked list comparing the file names. As the list was replaced by a hash table, the lookup time dropped significantly when used on systems with a large number of open tables. The performance on smaller installations remains in the same ballpark. Instead of growing exponentially, the lookup time now grows more or less linearly, at least in the ballpark with less than 100 000 open tables. @ storage/myisam/myisamchk.c Need to initialize MyISAM open table hash @ storage/myisam/myisampack.c Need to initialize MyISAM open table hash modified: storage/myisam/ha_myisam.cc storage/myisam/mi_close.c storage/myisam/mi_dbug.c storage/myisam/mi_keycache.c storage/myisam/mi_open.c storage/myisam/mi_panic.c storage/myisam/mi_static.c storage/myisam/myisamchk.c storage/myisam/myisamdef.h storage/myisam/myisampack.c === modified file 'storage/myisam/ha_myisam.cc' --- a/storage/myisam/ha_myisam.cc 2010-07-13 17:29:44 +0000 +++ b/storage/myisam/ha_myisam.cc 2010-10-21 10:51:58 +0000 @@ -2099,6 +2099,10 @@ static int myisam_init(void *p) myisam_hton->create= myisam_create_handler; myisam_hton->panic= myisam_panic; myisam_hton->flags= HTON_CAN_RECREATE | HTON_SUPPORT_LOG_TABLES; + + if (mi_init_open_table_hash(table_cache_size)) + return 1; + return 0; } === modified file 'storage/myisam/mi_close.c' --- a/storage/myisam/mi_close.c 2010-07-08 21:20:08 +0000 +++ b/storage/myisam/mi_close.c 2010-10-21 10:51:58 +0000 @@ -54,7 +54,9 @@ int mi_close(register MI_INFO *info) info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED); } flag= !--share->reopen; - myisam_open_list=list_delete(myisam_open_list,&info->open_list); + + my_hash_delete(&myisam_open_table_hash, (uchar *) info); + mysql_mutex_unlock(&share->intern_lock); my_free(mi_get_rec_buff_ptr(info, info->rec_buff)); === modified file 'storage/myisam/mi_dbug.c' --- a/storage/myisam/mi_dbug.c 2010-06-07 12:05:34 +0000 +++ b/storage/myisam/mi_dbug.c 2010-10-21 10:51:58 +0000 @@ -181,14 +181,14 @@ void _mi_print_key(FILE *stream, registe my_bool check_table_is_closed(const char *name, const char *where) { char filename[FN_REFLEN]; - LIST *pos; + uint idx; DBUG_ENTER("check_table_is_closed"); (void) fn_format(filename,name,"",MI_NAME_IEXT,4+16+32); mysql_mutex_lock(&THR_LOCK_myisam); - for (pos=myisam_open_list ; pos ; pos=pos->next) + for (idx= 0; idx < myisam_open_table_hash.records; ++idx) { - MI_INFO *info=(MI_INFO*) pos->data; + MI_INFO *info=(MI_INFO*) my_hash_element(&myisam_open_table_hash, idx); MYISAM_SHARE *share=info->s; if (!strcmp(share->unique_file_name,filename)) { === modified file 'storage/myisam/mi_keycache.c' --- a/storage/myisam/mi_keycache.c 2009-12-05 01:26:15 +0000 +++ b/storage/myisam/mi_keycache.c 2010-10-21 10:51:58 +0000 @@ -137,16 +137,16 @@ int mi_assign_to_key_cache(MI_INFO *info void mi_change_key_cache(KEY_CACHE *old_key_cache, KEY_CACHE *new_key_cache) { - LIST *pos; + uint idx; DBUG_ENTER("mi_change_key_cache"); /* Lock list to ensure that no one can close the table while we manipulate it */ mysql_mutex_lock(&THR_LOCK_myisam); - for (pos=myisam_open_list ; pos ; pos=pos->next) + for (idx= 0; idx < myisam_open_table_hash.records; ++idx) { - MI_INFO *info= (MI_INFO*) pos->data; + MI_INFO *info=(MI_INFO*) my_hash_element(&myisam_open_table_hash, idx); MYISAM_SHARE *share= info->s; if (share->key_cache == old_key_cache) mi_assign_to_key_cache(info, (ulonglong) ~0, new_key_cache); === modified file 'storage/myisam/mi_open.c' --- a/storage/myisam/mi_open.c 2010-07-23 20:59:42 +0000 +++ b/storage/myisam/mi_open.c 2010-10-21 10:51:58 +0000 @@ -36,24 +36,76 @@ if (pos > end_pos) \ goto err; \ } +/* + Get the value used as hash key (helper function for the + open table hash). Function is used as a callback + from the hash table +*/ +static uchar *mi_open_table_hash_key(const uchar *record, size_t *length, + my_bool not_used __attribute__((unused))) +{ + MI_INFO *info= (MI_INFO *) record; + *length= info->s->unique_name_length; + return (uchar*) info->s->unique_file_name; +} -/****************************************************************************** -** Return the shared struct if the table is already open. -** In MySQL the server will handle version issues. -******************************************************************************/ +/** + Initialize open table hash + + Function is normally called from myisam_init + with the system variable table_cache_size used + as hash_size. + + @param[in] hash_size Initial has size (elements) + @return inidicates success or failure of initialization + @retval 0 success + @retval 1 failure +*/ +int mi_init_open_table_hash(ulong hash_size) +{ + if (hash_size == 0) + hash_size= 32; /* default hash size */ + + if (my_hash_init(&myisam_open_table_hash, &my_charset_filename, + hash_size, 0, 0, mi_open_table_hash_key, 0, 0)) + return 1; /* error */ + + return 0; +} + + +/** + Retrieve the shared struct if the table is already + open (i.e in the open table hash) + + @param[in] filename table file name + @return shared struct, 0 if not in the cache +*/ MI_INFO *test_if_reopen(char *filename) { - LIST *pos; + HASH_SEARCH_STATE current_record; + int len= strlen(filename); - for (pos=myisam_open_list ; pos ; pos=pos->next) + MI_INFO *info= (MI_INFO*) my_hash_first(&myisam_open_table_hash, + (uchar *) filename, + len, ¤t_record); + /* + There might be more than one instance of a table share for + a given table in the hash table. We're interested in the one with + last_version set, so we iterate until we find it + */ + while (info) { - MI_INFO *info=(MI_INFO*) pos->data; MYISAM_SHARE *share=info->s; - if (!strcmp(share->unique_file_name,filename) && share->last_version) - return info; + if (share->last_version) + break; + + info= (MI_INFO*) my_hash_next(&myisam_open_table_hash, + (uchar *) filename, + len, ¤t_record); } - return 0; + return info; } @@ -628,8 +680,9 @@ MI_INFO *mi_open(const char *name, int m #ifdef THREAD thr_lock_data_init(&share->lock,&m_info->lock,(void*) m_info); #endif - m_info->open_list.data=(void*) m_info; - myisam_open_list=list_add(myisam_open_list,&m_info->open_list); + + if (my_hash_insert(&myisam_open_table_hash, (uchar *) m_info)) + goto err; mysql_mutex_unlock(&THR_LOCK_myisam); === modified file 'storage/myisam/mi_panic.c' --- a/storage/myisam/mi_panic.c 2009-12-05 01:26:15 +0000 +++ b/storage/myisam/mi_panic.c 2010-10-21 10:51:58 +0000 @@ -26,90 +26,102 @@ int mi_panic(enum ha_panic_function flag) { int error=0; - LIST *list_element,*next_open; MI_INFO *info; + uint idx; DBUG_ENTER("mi_panic"); mysql_mutex_lock(&THR_LOCK_myisam); - for (list_element=myisam_open_list ; list_element ; list_element=next_open) + if (my_hash_inited(&myisam_open_table_hash)) { - next_open=list_element->next; /* Save if close */ - info=(MI_INFO*) list_element->data; - switch (flag) { - case HA_PANIC_CLOSE: - mysql_mutex_unlock(&THR_LOCK_myisam); /* Not exactly right... */ - if (mi_close(info)) - error=my_errno; - mysql_mutex_lock(&THR_LOCK_myisam); - break; - case HA_PANIC_WRITE: /* Do this to free databases */ -#ifdef CANT_OPEN_FILES_TWICE - if (info->s->options & HA_OPTION_READ_ONLY_DATA) - break; -#endif - if (flush_key_blocks(info->s->key_cache, info->s->kfile, FLUSH_RELEASE)) - error=my_errno; - if (info->opt_flag & WRITE_CACHE_USED) - if (flush_io_cache(&info->rec_cache)) - error=my_errno; - if (info->opt_flag & READ_CACHE_USED) + if (flag == HA_PANIC_CLOSE) + { + while (myisam_open_table_hash.records) { - if (flush_io_cache(&info->rec_cache)) - error=my_errno; - reinit_io_cache(&info->rec_cache,READ_CACHE,0, - (pbool) (info->lock_type != F_UNLCK),1); + /* + As long as there are records in the hash, fetch the + first, and close it. + */ + info=(MI_INFO*) my_hash_element(&myisam_open_table_hash, 0); + mysql_mutex_unlock(&THR_LOCK_myisam); /* Not exactly right... */ + if (mi_close(info)) + error= my_errno; + mysql_mutex_lock(&THR_LOCK_myisam); } - if (info->lock_type != F_UNLCK && ! info->was_locked) + (void) mi_log(0); /* Close log if neaded */ + ft_free_stopwords(); + } + else + { + for (idx= 0; idx < myisam_open_table_hash.records; ++idx) { - info->was_locked=info->lock_type; - if (mi_lock_database(info,F_UNLCK)) - error=my_errno; - } + info=(MI_INFO*) my_hash_element(&myisam_open_table_hash, idx); + switch (flag) { + case HA_PANIC_CLOSE: break; /* To eliminate warning. Handled above */ + case HA_PANIC_WRITE: /* Do this to free databases */ #ifdef CANT_OPEN_FILES_TWICE - if (info->s->kfile >= 0 && mysql_file_close(info->s->kfile, MYF(0))) - error = my_errno; - if (info->dfile >= 0 && mysql_file_close(info->dfile, MYF(0))) - error = my_errno; - info->s->kfile=info->dfile= -1; /* Files aren't open anymore */ - break; + if (info->s->options & HA_OPTION_READ_ONLY_DATA) + break; #endif - case HA_PANIC_READ: /* Restore to before WRITE */ + if (flush_key_blocks(info->s->key_cache, info->s->kfile, FLUSH_RELEASE)) + error=my_errno; + if (info->opt_flag & WRITE_CACHE_USED) + if (flush_io_cache(&info->rec_cache)) + error=my_errno; + if (info->opt_flag & READ_CACHE_USED) + { + if (flush_io_cache(&info->rec_cache)) + error=my_errno; + reinit_io_cache(&info->rec_cache,READ_CACHE,0, + (pbool) (info->lock_type != F_UNLCK),1); + } + if (info->lock_type != F_UNLCK && ! info->was_locked) + { + info->was_locked=info->lock_type; + if (mi_lock_database(info,F_UNLCK)) + error=my_errno; + } #ifdef CANT_OPEN_FILES_TWICE - { /* Open closed files */ - char name_buff[FN_REFLEN]; - if (info->s->kfile < 0) - if ((info->s->kfile= mysql_file_open(mi_key_file_kfile, - fn_format(name_buff, - info->filename, "", - N_NAME_IEXT, 4), - info->mode, MYF(MY_WME))) < 0) - error = my_errno; - if (info->dfile < 0) - { - if ((info->dfile= mysql_file_open(mi_key_file_dfile, - fn_format(name_buff, - info->filename, "", - N_NAME_DEXT, 4), - info->mode, MYF(MY_WME))) < 0) - error = my_errno; - info->rec_cache.file=info->dfile; - } - } + if (info->s->kfile >= 0 && mysql_file_close(info->s->kfile, MYF(0))) + error = my_errno; + if (info->dfile >= 0 && mysql_file_close(info->dfile, MYF(0))) + error = my_errno; + info->s->kfile=info->dfile= -1; /* Files aren't open anymore */ + break; #endif - if (info->was_locked) - { - if (mi_lock_database(info, info->was_locked)) - error=my_errno; - info->was_locked=0; + case HA_PANIC_READ: /* Restore to before WRITE */ +#ifdef CANT_OPEN_FILES_TWICE + { /* Open closed files */ + char name_buff[FN_REFLEN]; + if (info->s->kfile < 0) + if ((info->s->kfile= mysql_file_open(mi_key_file_kfile, + fn_format(name_buff, + info->filename, "", + N_NAME_IEXT, 4), + info->mode, MYF(MY_WME))) < 0) + error = my_errno; + if (info->dfile < 0) + { + if ((info->dfile= mysql_file_open(mi_key_file_dfile, + fn_format(name_buff, + info->filename, "", + N_NAME_DEXT, 4), + info->mode, MYF(MY_WME))) < 0) + error = my_errno; + info->rec_cache.file=info->dfile; + } + } +#endif + if (info->was_locked) + { + if (mi_lock_database(info, info->was_locked)) + error=my_errno; + info->was_locked=0; + } + break; + } } - break; } } - if (flag == HA_PANIC_CLOSE) - { - (void) mi_log(0); /* Close log if neaded */ - ft_free_stopwords(); - } mysql_mutex_unlock(&THR_LOCK_myisam); if (!error) DBUG_RETURN(0); === modified file 'storage/myisam/mi_static.c' --- a/storage/myisam/mi_static.c 2010-08-05 12:34:19 +0000 +++ b/storage/myisam/mi_static.c 2010-10-21 10:51:58 +0000 @@ -22,7 +22,7 @@ #include "myisamdef.h" #endif -LIST *myisam_open_list=0; + uchar myisam_file_magic[]= { (uchar) 254, (uchar) 254,'\007', '\001', }; uchar myisam_pack_file_magic[]= @@ -40,6 +40,9 @@ ulong myisam_concurrent_insert= 0; ulonglong myisam_max_temp_length= MAX_FILE_SIZE; ulong myisam_data_pointer_size=4; ulonglong myisam_mmap_size= SIZE_T_MAX, myisam_mmap_used= 0; +HASH myisam_open_table_hash; + + static int always_valid(const char *filename __attribute__((unused))) { === modified file 'storage/myisam/myisamchk.c' --- a/storage/myisam/myisamchk.c 2010-07-23 20:16:29 +0000 +++ b/storage/myisam/myisamchk.c 2010-10-21 10:51:58 +0000 @@ -88,6 +88,13 @@ int main(int argc, char **argv) get_options(&argc,(char***) &argv); myisam_quick_table_bits=decode_bits; error=0; + + if (mi_init_open_table_hash(0)) + { + fprintf(stderr, "Can't initialize MyISAM storage engine\n"); + exit(-1); + } + while (--argc >= 0) { int new_error=myisamchk(&check_param, *(argv++)); === modified file 'storage/myisam/myisamdef.h' --- a/storage/myisam/myisamdef.h 2010-07-26 11:34:07 +0000 +++ b/storage/myisam/myisamdef.h 2010-10-21 10:51:58 +0000 @@ -25,6 +25,7 @@ #include #endif #include +#include "hash.h" /* undef map from my_nosys; We need test-if-disk full */ #if defined(my_write) @@ -287,7 +288,6 @@ struct st_myisam_info { uint data_changed; /* Somebody has changed data */ uint save_update; /* When using KEY_READ */ int save_lastinx; - LIST open_list; IO_CACHE rec_cache; /* When cacheing records */ uint preload_buff_size; /* When preloading indexes */ myf lock_wait; /* is 0 or MY_DONT_WAIT */ @@ -477,7 +477,7 @@ extern mysql_mutex_t THR_LOCK_myisam; /* Some extern variables */ -extern LIST *myisam_open_list; +extern HASH myisam_open_table_hash; extern uchar myisam_file_magic[], myisam_pack_file_magic[]; extern uint myisam_read_vec[], myisam_readnext_vec[]; extern uint myisam_quick_table_bits; @@ -759,6 +759,7 @@ my_bool mi_check_status(void* param); void mi_disable_non_unique_index(MI_INFO *info, ha_rows rows); extern MI_INFO *test_if_reopen(char *filename); +extern int mi_init_open_table_hash(ulong size); my_bool check_table_is_closed(const char *name, const char *where); int mi_open_datafile(MI_INFO *info, MYISAM_SHARE *share, const char *orn_name, File file_to_dup); === modified file 'storage/myisam/myisampack.c' --- a/storage/myisam/myisampack.c 2010-07-23 20:17:55 +0000 +++ b/storage/myisam/myisampack.c 2010-10-21 10:51:58 +0000 @@ -210,6 +210,13 @@ int main(int argc, char **argv) if (load_defaults("my",load_default_groups,&argc,&argv)) exit(1); + if (mi_init_open_table_hash(0)) + { + fputs("Can't initialize MyISAM storage engine", stderr); + exit(1); + } + + default_argv= argv; get_options(&argc,&argv); --===============1518316327287374743== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/magne.mahre@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: magne.mahre@stripped # target_branch: file:///export/home/tmp/x/mysql-next-mr-bugfixing-\ # 49177/ # testament_sha1: 631a72b618aa78e30ee6c9e0785d5d985f86dabb # timestamp: 2010-10-21 12:52:05 +0200 # base_revision_id: anitha.gopi@stripped\ # ki3juo8ezw678ndl # # Begin bundle IyBCYXphYXIgcmV2aXNpb24gYnVuZGxlIHY0CiMKQlpoOTFBWSZTWSN2CsQAC2j/gHf0gEF5//// f+/e7r////pgFX1233nNdL3no57Wru4RWFIJQo22HN1TjsNMihnbXbabNtoNmFmgaTazs53Wh0qE ihAmU9CaZU/TA1NDJiptNI0ntT0KabSHqHqZAY0am9U9AlCAE0TIJqeIo2jKB6mgABoBoA0AAPUC KnpjSSep5JvUhtJk02JMjQ0xpGEyYAjJkaYI9TahgkRCAgI00JqZhMJT9U/VP1NJ+qPTKAPRGnqA GjRoGQ5k00MgAYjIMgBpggYgGjTQAZA0ACSQCJgEAENDKMhMIQ0YCEfqaI9GoAaZqNJgxYsR8Ggl pADF3fZ6iSXZR9rv6Ynn5u7s6uwv0V07yvfRw3zb7PJXgwMb5FshgZJkmToIfh+nj9/KY/H7vh5R 1LOpPGl59gHXxEHAZAizUswNkQhY5TKrkUAotmDYMCKBWbVpRn5yYiigo78aYO9hhYbG4bCHDJ+9 jYwPGRrKp58PU6uuHvxk81WpnW7H8Z4ugMF+1TBkQI+ueNbbXEUsurifvl5dzsnSh3Rwr87K+dzx Iqk5FVBE46kHC8C+UlyZ6viZMz8WlGTrOJOtNn80K0hNYRatiS662xm+CdehuHAgJppx5IWnaXfU GFSs2kECswEk0I6mGX6oUNfU0BRgAVEueCBsTRoY22njCRk0B7oK5KPT9Z8OQtYbdT71R2pg6YeS pKKvYXJkD2rJzD/OLs3ZtjobymHdS83qDY4PWIgk8BHCIY2m022222m0m0Ju/+kv4hFGGdyX/kg2 s3de2VHZKOGXPybrKap53VjA3DmfVJaIq7Wi4zJuKEMwd7zawxgL01OiJw0kmFICjJavvr6qGFtp UyrnXGHKly5eDo5drlQQVFVNRzmk+KhlB7GTBrW8xcxCSxvqlQotgCAKVCAkgKgggkKUrbTi665D nAeE8ExwOgTnkF8WFQF3BpvtTvwyWqis/DbrxkiHmbjBxmX7NWMYVz1+t022aNFRNH9aaJFLIkDn ddSc3RYdpNeHwu2atCxpS0WSzMv3XeerKoqfSEpJ7Bf7aDroa5OxRdIX9/Je7uJE/WIxdqsq8HJm sBWNrGObDbRqG/SNoVwEKuI6NEskUQg+cmsuE93c2yUkZyMp0lRmEXmKOZ49yXBCLn6sIm5SNBWM lnPTVHfvi7UT7JbEiRWC3WzR3WhelHMGRiq4ccnZBw4bpu+H09OjqqyRywQckRFm0RZmzcY2zV4H ymfWZLkBR/OeKGwEXfOl1STGEwxpjEUVLm3krZzqGi4/5ad5/E0TbSDKGnnnQ0LJMemAyY5zIjxR KHytvvXxaQR5T229M6uroU9LLOL/HvNn2PR9GBq6F7HxeGXfQ8kZtLm135RY10918WNtlBSx8pda t/AVUvZfyeWmjppZL0mEYjMqn4CcR6mH2NEBpHuWezXub2dDc0belmjkKLqwj0Wn6iEVpawU33br s4rfy20ywGXyhYMSj2wMKEEDTxIF3krA2JWQSgxJhka5LYMvYUAWzXU9fJMFBgPHLxv2bRD7UGYg phnZmObZ4H+Ejy03nhCMnLPa97D0YVHDxwxdKFQ4KJuASuj1rh3KlECkOsS5unq9/zHzj+9YsMFo 5sgWw+4I8w3YSN8QHMwhpHG4ftGiWlRqjEN1fP6b5F1ofuYTaKgixRJtiZ6W4SAqCJSSOo9JDoQt DAWQwF5y4gEUFmSGGJjXoYuASDmFs1IkBWZaERgmRRiX+FBxAVKkwQqCTT18Q7pYyJmWJgpUuYDX v290xfEYQJqgTewSwHGb2u0xDBdPHscIQ7FvA/npISSPnaQtwCYItyFdYC1mED7DV10sAlCMOGtp 7AklTuT29ZYzqM+1Ird60rPJhij9IsUZWNTN4bAUFpZ4fTlaY4klgAp3F+JQgQLY2kS883KPAoGh g9k4WkDeCPq11yMFrYGlMuEVGw36bSBuKh5sEONpAzN/zukJGnvw7csjTfpWcFhHF0ZWxIs7DlKV hRjeuDkgKEE1ZsNBOxMAlL3vwR+BiYjEzkYuRakmHFBxaxchEhI86xiQN++PhAMCpOoxOdq0d7tt ELTEx0PMjEY7DYZlRddQq9v5kyYxrInb9puELjxN5WYnwNxYzNp33vVpgIgpFEQzg3IWW2iUmZnP hwuUMFKtVjpN9qvkGUzCWYhlQhlfi5BR5MOVbFDIgRIE5mQ4gcotsKnSn/G1OKRM9mJhk92T4atF Sas+Q9pEcRzgTEco7UPSWkShCDx5skXOYYl0FZ1KupYapbq3EiEzBjMcXgP+/lepDNF5E0Qycaxt ZkGhoaD4ndKli1HI8D4G0R42EA3m/fqrz3Z72hB75LAnAj2GsgVRmMAshZxkQKFRTdROqqLdZtXI qmVYjsWtEiAj5qXI4gg4Hd6FDvG01GhmRKjypsIobFA4rkeoylry2NnpdG+DpF994wbDY4nhF7Gg zNQ+OiRTlSNzEQeSLyA8vNW7AYi/E8IGF5sX3ll9rZnCyNjkiqTjqYSPERsEd5eYEihAzIaBmOMR G8tccjsWKrVjyBgTo27Is056WxhuhDc+ucaUBFXZiwVDdgPHIor8zjcoYYm85BNRTyzEyuXMZEBE Dct92cC7f6d8AWORxLy20Y09mgpGhqNJcXiO47zmai6qvRshB+xsZtYAnBrHxHEZHabSk5EihQyO b/SkijTkQtaJMuLopbTg4TMKbix9pJgUNRYKjvvjlzNwyhid3cpJN9lbTiEN7tpGV9T+hzhHqIm2 gzhWeSFKGZPWTMiwyXphgNQky1+UXlMROBFhXMcX3gi8N2UCI2qlxY+Gs34mWDjTowv7dfJ+P2XO 7VwAZ5tEPkgopUTtOTfEWZapRSkRaQVSKuAWi1cEK+9+zD1OPQ225fxd8dzCWRAaZg+jbA3z0d8F xdaZ71pzsuNL1pDyWHwPk1qfoJ07LwqdvujQDAbaHIjbCBgjMHYiQoRx9YiY2i3NJBUVrjMH0nYb pu5WoF6v+raHwF+R+YSUj3LjRBH/NZeabSaBsc/0e5cq/Jf0Xw+C/gSIhyJ1FIjJ+CvMz8TF77Qa 3Bw8fmmWd86ih2himWP+7kQbxKhkbmRNEwHjO7UkFfwRUQl4hNiwSKKpFFdViqyI+OsVgx8ajuHC cDHByVRWkIh2pyZLh7XsvxqVs5EOJhgoMQYJBZuZ7JknAeUFObSvHjM69fmP2zcx+R2TWpcDXbBG PBOHsxabgix08jn6SyrvKuw73RTZJHcPJXrSikDcta4qsyLaeDT0ErwX6SWSR2wNfKNeuWFBHvGT B30ZvHRCKoGNkyIgRxlbkMKNQiiS/BB7UPA/oVEeKZcyqvRgAQiFfWBDKttQIool5JFirmVvy4eh 7GEZdwixRGCrGI+s8DpMh9Y5xA/QtR9J9xEY0ffoX3H4WmlcWEtzCS0/SVkbAXxY/w/AWQcNmwzU jTA+0Y2D7RFw8maSJeFgdqZJHu5oFxATnIFxGtRA2afiOS2vJZjQ2k7EBYMgXe1HJIqejidSEK51 Hq4Kn7FToWT3iJJdPFGBmQWacpIprhcPCHwupdIxzEIyDcrnVIE6oZiubLissKtBHMO+ry3ToFVY fwLun4czYS6m00FgxBVngKbDfYeBUSIlZsKjwHZ/gbvPAc3U5Ksl7NmCJpLRFVYxCOI1ooLSGuXA r+34Hvt8xz9hWsy9vk2FJjLs6BeZgiePBc7SYwDaSF3HyALk6HhQ5mZw6HM9BCo6qJ1O4yeOJFvU LS08TbwS5Jl7Xk9f5J7mtOhUdBHBOLD8TuN5qPUVmqVRmTPYX0bGYujaa0l4ImOPIZFHJHqV5ANV BNO8SDhH22iNJOWjVWq32FWA5jQuYsvNJsG7zSyNXmdU3gI6jHuJF+/0tb8muXMy6GZKWJIbGs5D jMFzOR6BHuHGkqPQazy26wWZZqNJcbjBCKFxaczjrDpxTEE1x27ChpXBV0h2FhsHlhd/kTYPgY61 60i9wWHFe2/WKk6k5Jdt5dLnXm3Jw6DdcD2c5jWzeM+DBRtYUMFll2F6R3WiwxtVvpKwtNppDc75 Jv56uebOCjG7MycToe+11py0H66KlEWJMYxIRDJqHstWfZpQUUYFqgIwhc1Dc/OZivgdV/Jnfp6q renpeadK4H6RF/iY7zKnHGtsWGEOEONyBa6pwIjfNlZHmYINCFLERLOAux2F+FckoZd2m54NY4Rv 3tJjJhzq/CBlMvIxUSTjOMrzrs1+mP1FF7VTfeAUz1rFpEZK6eSA6dLe6HYHy2b9HmzWWNEBvPVp bGEVrKYwbSQ0v5K8hT8QVEtl6mQXQ3rOAhxOmsv53l40u5xEvzEN7T+p8/c9lmqq7nMaGYtS7bpN yDFP9nxqN9Ep0HAyFaRFCqMWkww0EkIeekQ4v9aqQlzTe9MMN5yxOokSXmR5nw8OZE/QeweUYmeb BeVj3nDsKyouVR8uwssJEu0zVHpF3iegeVgVuNv/Ii7lpRPdIiFPk+5VLCIB+EQO0ZD7u30CMakU Hna3H2/42iBVr1OrfC8O/vLCMmUVk8YGTQuB2nqOpX0cWaoRiRWcm8CRQusoMTBK+q0hMvoXp9BC l4967VriAsgFwNBUtojSonZ907IhhEPIkCWIgmESbCEdLJGkqMUJiRxykSSCSHYloQ22JtTsIDEr tMfF4vLiYiLVbYyS+IyHCXKzsIRcpD3FCaO4/SI+gziFtNF4BZjegDYF+e0zLlEafOvmSS1TpNtl zGdu7vmlNVguSRKGpe9MWBUZhFVaxGv0HfVCANr6oqI1Jh6VQkW4i9uz28xjQAe3XqJg4W42loP9 FuGRXMhain5Ks7oaP9j2d8CPz+eJ7FVBUs1OA2aToh7zXdAenhz69d03jkNzFYjL03SkfuTxss5v DyzUL0mENSSZ2mw6TNEv8iSR4IVZk2qJNxEC4nEBAjrGho8fEmWSiDIYQN5/X6MQFox+/4Fp9Pum VNgdpyU19itNP1dto5h1TmTJmcRd7io5oejM2mnRBHPpX1NDGwx9ioJIWuRL9/r6dYivODWzMcql S8qkFy70xHReUlxnFV6Jg58lpRkvF8owZsImxDSGMaG3SEBG8YojTSQwC9Gl4JsCHp/PupqS7c7r iUqYq5jUoRRNJLgxY+BAruZkTRXVB0DDP4slcW3LQeI+kVzJywNqj18zHZBjuLCo3PQghJe6skOT a0B5H5khiPP49aBZZZFRo4TkMUVPGEcIwONNJacURiqnx6u4bIVwyZIJmS6MYjZvjPw9eWwybjcd JVFmAU2BI5KTpO78Oyo7xtsaW5OyS85FRFIX1viyVogbUZ/YVD2HeeJs6Da+V8LUqkDJpCoIhKI+ 2hASwbXSAEeo6ikiLgoRKSM2lugRwjh+WN+YQvQSs1JAlgvmJffmVhAni5MuKUgUttteRgQ1gqzf nkVoga+mEPgimNbCGZ9rVvk1nWelYVb1RBo0ECoIGPRnCloWR7CtFq7bt/j72H0+FAEbB3LFR3iG S0JxrPehUNRxhiiJgsBHzCPm9e0VD5kINI00YpiTPLkzU0KyHUXVSOgYxCjSn8a5NwSixEC7gokL EXQw3FmU8/HQWuinUg3/tlOJpw3Ec2+heRkfdLkDYsgDU5Gxaz1wEMChkPeed4CikfKssj/pkkGF dbw29ptxJ1RJQ3UL61FQdx6jKSPWaBHCuwRoNhOYDerjQg1LpKhmcBbQ6wbGig4gcOrfNTFm0pYX gmklcarcslEjCzI5pfq4TSixzIFlKUYTaGw79MH4BUaD68BGALJMCdYYdB59gzjn73pEZ57xz9F7 Sh4MfaQdzPlrS3CrZooxEVFMyx/hFIuOSqVaZCK2XcM5cMDid41D06em0ylRmDF73x/TwPHQkRdE dAnenWrXCKlZktVpQq0U8isEIytDvBpnVY2xoxIbg3n5YUZOuKSWVNIoGNpUIwsXOIpOL1u9QzUh Z0LkNoT2I2tLltS8e44TvPaI9rbbbbbbb1xDcSI3peJjeNYvU9ZJNn07Fp4KVyHx2sWpLUIi9ZjG XgAEqz6IERimBXf5Rt41ryhDzJxRnP1KdC22yBiZSEoSjaXOEeg65iEhFSYIYVKligNmKSHKCVNH cPMyhD669F8ypoDbX3SKrqFoI3VLXB23jIVkvM0BTOiVpDiIrbkNNt64ol851soISeve/mZaZSYy dcwLIwtmgyuiR2m1fMrDE+Q9x3kDpvDUNkWxaRqX82Ca96zMsdBp1FgAQ+J5mrRTUvUFRV6Mjglc qq2LmpJPDH1nZkcTM3HCAjaAry04ngpSf4psFfiTNSiQCaF0yNuJrJHoteO45zrEdC4LQZA1IvKd RNgza/dMr0STpNWjXRyPcSXiciixQVfOch4uS3G0RQQ4hbKR5lRpHmBz4PQJ5vzJHQ54F1O7pNoW ug0WZiJbcdhI/IVFzaQxiKiC6CKSRAt1F8roWBUIJillxhORtOBrOBtzBt0Rkl1ZCtKzxAVY8t89 UTsKzNXGInWgvmc9uxfMdcHFfRtUnNBzHdOTs/YXckU4UJAjdgrE --===============1518316327287374743==--