From: Frazer Clement Date: June 29 2011 11:28pm Subject: bzr commit into mysql-5.1-telco-7.0 branch (frazer.clement:4487) List-Archive: http://lists.mysql.com/commits/140123 Message-Id: <201106292328.p5TNSHoW031878@acsmt356.oracle.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============6801600364784646883==" --===============6801600364784646883== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///home/frazer/bzr/mysql-5.1-telco-7.0/ based on revid:jonas.oreland@stripped 4487 Frazer Clement 2011-06-30 WL5353 Primary Cluster Conflict Detection Implement Ndb_slave_max_replicated_epoch status variable New status variable, initialised on Slave start from ndb_apply_status table contents and maintained as 'reflected' ndb_apply_status WRITE_ROW events are applied. ndb_apply_status events logged by the Slave server, or any server in the IGNORE_SERVER_IDs list are used. The status variable is reset when RESET SLAVE is executed. Additionally : - Ndb handler code accessing the MasterInfo structure is factored out into new files (ndb_mi.h, ndb_mi.cc) - The master_epoch member of MasterInfo is no longer used/maintained. Two new testcases added to verify status variable initialisation and maintenance. This variable is used in a future patch series to detect distributed update serialisation violations (conflicts). added: mysql-test/suite/ndb_rpl/r/ndb_rpl_circular_2ch_rep_status.result mysql-test/suite/ndb_rpl/r/ndb_rpl_init_rep_status.result mysql-test/suite/ndb_rpl/t/ndb_rpl_circular_2ch_rep_status.cnf mysql-test/suite/ndb_rpl/t/ndb_rpl_circular_2ch_rep_status.test mysql-test/suite/ndb_rpl/t/ndb_rpl_init_rep_status.test sql/ndb_mi.cc sql/ndb_mi.h modified: mysql-test/suite/ndb/r/ndb_basic.result sql/Makefile.am sql/ha_ndbcluster.cc sql/ha_ndbcluster.h sql/ha_ndbcluster_binlog.cc storage/ndb/CMakeLists.txt === modified file 'mysql-test/suite/ndb/r/ndb_basic.result' --- a/mysql-test/suite/ndb/r/ndb_basic.result 2011-06-16 18:16:01 +0000 +++ b/mysql-test/suite/ndb/r/ndb_basic.result 2011-06-29 23:28:01 +0000 @@ -76,6 +76,7 @@ Ndb_number_of_data_nodes # Ndb_number_of_ready_data_nodes # Ndb_pruned_scan_count # Ndb_scan_count # +Ndb_slave_max_replicated_epoch # SHOW GLOBAL VARIABLES LIKE 'ndb\_%'; Variable_name Value ndb_autoincrement_prefetch_sz # === added file 'mysql-test/suite/ndb_rpl/r/ndb_rpl_circular_2ch_rep_status.result' --- a/mysql-test/suite/ndb_rpl/r/ndb_rpl_circular_2ch_rep_status.result 1970-01-01 00:00:00 +0000 +++ b/mysql-test/suite/ndb_rpl/r/ndb_rpl_circular_2ch_rep_status.result 2011-06-29 23:28:01 +0000 @@ -0,0 +1,169 @@ +include/rpl_init.inc [topology=1->2,4->3] +include/rpl_connect.inc [creating master] +include/rpl_connect.inc [creating master1] +include/rpl_connect.inc [creating slave] +include/rpl_connect.inc [creating slave1] +include/rpl_start_slaves.inc +Cluster A servers have no epoch replication info +select count(1) from mysql.ndb_apply_status; +count(1) +0 +Cluster A servers have no max replicated epoch value +Master(1) +select variable_name, variable_value from information_schema.global_status +where variable_name='Ndb_slave_max_replicated_epoch'; +variable_name variable_value +NDB_SLAVE_MAX_REPLICATED_EPOCH 0 +Master1(3) +select variable_name, variable_value from information_schema.global_status +where variable_name='Ndb_slave_max_replicated_epoch'; +variable_name variable_value +NDB_SLAVE_MAX_REPLICATED_EPOCH 0 +Make a change originating at Cluster A +Master(1) +use test; +create table t1 (a int primary key, b varchar(100)) engine=ndb; +insert into t1 values (1, "Venice"); +Allow it to propagate to Cluster B +Originate a second unrelated change at Cluster B, to allow us to wait for +reverse propagation in the testcase +Slave1 (4) +insert into t1 values (2, "Death"); +Allow it to propagate to Cluster A +Observe new entry in ndb_apply_status on Cluster A +Master (1) +select server_id from mysql.ndb_apply_status order by server_id; +server_id +1 +4 +Non-slave server on Cluster A will have no value for Max Replicated Epoch +select variable_name, variable_value from information_schema.global_status +where variable_name='Ndb_slave_max_replicated_epoch'; +variable_name variable_value +NDB_SLAVE_MAX_REPLICATED_EPOCH 0 +Slave server on Cluster A has current value for Max Replicated Epoch +Master1 (3) +Expect count 1 +select +count(1) +from +information_schema.global_status, +mysql.ndb_apply_status +where +server_id = 1 +and +variable_name='Ndb_slave_max_replicated_epoch' + and +variable_value = epoch; +count(1) +1 +Now wait for all replication to quiesce +Now swap replication channels around +include/rpl_stop_slaves.inc +include/rpl_change_topology.inc [new topology=2->1,3->4] +Get current master status on Cluster A new master (next pos in Binlog) +Master1 (3) +Flush logs to ensure any pending update (e.g. reflected apply_status write row) +is skipped over. +flush logs; +Setup slave on Cluster B to use it +Slave1 (4) +Get current master status on Cluster B new master (next pos in Binlog) +Slave (2) +Flush logs to ensure any pending update (e.g. reflected apply_status write row) +is skipped over. +flush logs; +Setup slave on Cluster A to use it +Master (1) +Master (1) +Show that Cluster A Slave server (old master) has no Max replicated epoch before receiving data +select variable_name, variable_value from information_schema.global_status +where variable_name='Ndb_slave_max_replicated_epoch'; +variable_name variable_value +NDB_SLAVE_MAX_REPLICATED_EPOCH 0 +Master1 (3) +Cluster A Master server (old slave) has old Max replicated epoch +select +count(1) +from +information_schema.global_status, +mysql.ndb_apply_status +where +server_id = 1 +and +variable_name='Ndb_slave_max_replicated_epoch' + and +variable_value = epoch; +count(1) +1 +Now start slaves up +include/rpl_start_slaves.inc +Show that applying something from Cluster B causes the +old Max Rep Epoch to be loaded from ndb_apply_status +There is no new Max Rep Epoch from Cluster A as it has not changed +anything yet +Slave (2) +insert into test.t1 values (3, "From the Sea"); +Allow to propagate to Cluster A +Master (1) +New Slave server on Cluster A has loaded old Max-Replicated-Epoch +select server_id from mysql.ndb_apply_status order by server_id; +server_id +1 +2 +4 +select +count(1) +from +information_schema.global_status, +mysql.ndb_apply_status +where +server_id = 1 +and +variable_name='Ndb_slave_max_replicated_epoch' + and +variable_value = epoch; +count(1) +1 +Now make a new Cluster A change and see that the Max Replicated Epoch advances +once it has propagated +Master1 (3) +insert into test.t1 values (4, "Brooke"); +Propagate to Cluster B +Make change on Cluster B to allow waiting for reverse propagation +Slave (2) +insert into test.t1 values (5, "Rupert"); +Wait for propagation back to Cluster A +Master (1) +Show that Cluster A now has 2 different server_id entries in ndb_apply_status +Those from the new master (server_id 3) are highest. +select server_id from mysql.ndb_apply_status order by server_id; +server_id +1 +2 +3 +4 +select +count(1) +from +information_schema.global_status, +mysql.ndb_apply_status +where +server_id = 3 +and +variable_name='Ndb_slave_max_replicated_epoch' + and +variable_value = epoch; +count(1) +1 +local_server_with_max_epoch +3 +Done +drop table t1; +include/rpl_stop_slaves.inc +CHANGE MASTER TO IGNORE_SERVER_IDS= (); +CHANGE MASTER TO IGNORE_SERVER_IDS= (); +CHANGE MASTER TO IGNORE_SERVER_IDS= (); +CHANGE MASTER TO IGNORE_SERVER_IDS= (); +include/rpl_start_slaves.inc +include/rpl_end.inc === added file 'mysql-test/suite/ndb_rpl/r/ndb_rpl_init_rep_status.result' --- a/mysql-test/suite/ndb_rpl/r/ndb_rpl_init_rep_status.result 1970-01-01 00:00:00 +0000 +++ b/mysql-test/suite/ndb_rpl/r/ndb_rpl_init_rep_status.result 2011-06-29 23:28:01 +0000 @@ -0,0 +1,78 @@ +include/master-slave.inc +[connection master] +reset master; +stop slave; +Generate something in the Masters Binlog +use test; +create table t1 (a int primary key, b int) engine=ndb; +insert into t1 values (1,1); +Initial state +select * from mysql.ndb_apply_status; +server_id epoch log_name start_pos end_pos +select variable_value from information_schema.global_status +where variable_name like '%Ndb_slave_max_replicated_epoch%'; +variable_value +0 +select @slave_server_id:=(variable_value+0) from information_schema.global_variables +where variable_name like 'server_id'; +@slave_server_id:=(variable_value+0) +2 +Default, no data, max replicated epoch will be 0. +reset slave; +start slave; +select server_id from mysql.ndb_apply_status order by server_id; +server_id +1 +select variable_value from information_schema.global_status +where variable_name like 'Ndb_slave_max_replicated_epoch'; +variable_value +0 +Default, load of own serverid from ndb_apply_status, should be 111 +drop table test.t1; +stop slave; +reset slave; +insert into mysql.ndb_apply_status values (@slave_server_id, 111, 'Fictional log', 222, 333); +start slave; +select server_id from mysql.ndb_apply_status order by server_id; +server_id +1 +2 +select variable_value from information_schema.global_status +where variable_name like 'Ndb_slave_max_replicated_epoch'; +variable_value +111 +drop table test.t1; +Check that reset slave resets Ndb_slave_max_replicated_epoch +stop slave; +select variable_value from information_schema.global_status +where variable_name like 'Ndb_slave_max_replicated_epoch'; +variable_value +111 +reset slave; +select variable_value from information_schema.global_status +where variable_name like 'Ndb_slave_max_replicated_epoch'; +variable_value +0 +Multiple-channel, load highest of configured serverids, should be 222 +set @other_local_server_id=@slave_server_id+1; +set @other_remote_server_id=@slave_server_id+2; +insert into mysql.ndb_apply_status values (@slave_server_id, 111, 'Fictional log', 222, 333); +insert into mysql.ndb_apply_status values (@other_local_server_id, 222, 'Fictional log', 222, 333); +insert into mysql.ndb_apply_status values (@other_remote_server_id, 444, 'Fictional log', 222, 333); +CHANGE MASTER TO IGNORE_SERVER_IDS=(3);; +start slave; +select server_id from mysql.ndb_apply_status order by server_id; +server_id +1 +2 +3 +4 +select variable_value from information_schema.global_status +where variable_name like 'Ndb_slave_max_replicated_epoch'; +variable_value +222 +stop slave; +CHANGE MASTER TO IGNORE_SERVER_IDS= (); +start slave; +drop table test.t1; +include/rpl_end.inc === added file 'mysql-test/suite/ndb_rpl/t/ndb_rpl_circular_2ch_rep_status.cnf' --- a/mysql-test/suite/ndb_rpl/t/ndb_rpl_circular_2ch_rep_status.cnf 1970-01-01 00:00:00 +0000 +++ b/mysql-test/suite/ndb_rpl/t/ndb_rpl_circular_2ch_rep_status.cnf 2011-06-29 23:28:01 +0000 @@ -0,0 +1,14 @@ +!include ndb_rpl_circular_2ch.cnf + +[mysqld.1.1] +ndb-log-apply-status + +[mysqld.2.1] +ndb-log-apply-status + +[mysqld.1.slave] +ndb-log-apply-status + +[mysqld.2.slave] +ndb-log-apply-status + === added file 'mysql-test/suite/ndb_rpl/t/ndb_rpl_circular_2ch_rep_status.test' --- a/mysql-test/suite/ndb_rpl/t/ndb_rpl_circular_2ch_rep_status.test 1970-01-01 00:00:00 +0000 +++ b/mysql-test/suite/ndb_rpl/t/ndb_rpl_circular_2ch_rep_status.test 2011-06-29 23:28:01 +0000 @@ -0,0 +1,247 @@ +--source include/have_ndb.inc +--source suite/ndb_rpl/ndb_master-slave_2ch.inc +--source include/have_binlog_format_mixed_or_row.inc + +# +# Test that the Maximum replicated epoch is maintained +# as expected in a circular, 2 channel configuration. +# The channels are swapped, and replication is restarted +# The MaxReplicatedEpoch is reloaded from ndb_apply_status +# for the Servers considered local (IGNORE_SERVER_IDS) +# +--connection master +--echo Cluster A servers have no epoch replication info +select count(1) from mysql.ndb_apply_status; + +--echo Cluster A servers have no max replicated epoch value +--echo Master(1) +select variable_name, variable_value from information_schema.global_status + where variable_name='Ndb_slave_max_replicated_epoch'; +--connection master1 +--echo Master1(3) +select variable_name, variable_value from information_schema.global_status + where variable_name='Ndb_slave_max_replicated_epoch'; + +--echo Make a change originating at Cluster A +--connection master +--echo Master(1) +use test; +create table t1 (a int primary key, b varchar(100)) engine=ndb; +insert into t1 values (1, "Venice"); + +--echo Allow it to propagate to Cluster B +--sync_slave_with_master slave + +--echo Originate a second unrelated change at Cluster B, to allow us to wait for +--echo reverse propagation in the testcase +--connection slave1 +--echo Slave1 (4) +insert into t1 values (2, "Death"); + +--echo Allow it to propagate to Cluster A +--sync_slave_with_master master1 + +--echo Observe new entry in ndb_apply_status on Cluster A +--connection master +--echo Master (1) +select server_id from mysql.ndb_apply_status order by server_id; + +--echo Non-slave server on Cluster A will have no value for Max Replicated Epoch +select variable_name, variable_value from information_schema.global_status + where variable_name='Ndb_slave_max_replicated_epoch'; + +--echo Slave server on Cluster A has current value for Max Replicated Epoch +--connection master1 +--echo Master1 (3) +--echo Expect count 1 +# Here we join the max rep epoch with ndb_apply_status for server id 1 +# (Our site's current master server) +select + count(1) + from + information_schema.global_status, + mysql.ndb_apply_status + where + server_id = 1 + and + variable_name='Ndb_slave_max_replicated_epoch' + and + variable_value = epoch; + +--echo Now wait for all replication to quiesce + +--echo Now swap replication channels around +--source include/rpl_stop_slaves.inc +--let $rpl_topology= 2->1,3->4 +--source include/rpl_change_topology.inc + +# We've changed the direction, but need to set binlog filenames +# and positions + +# +# 'Normally' we should use the ndb_apply_status max applied epoch, +# then lookup ndb_binlog_index etc. +# However, in this case (and probably in lots of real cases), no changes +# have been made after the last applied epoch, so there is no epoch +# after the current one, and therefore no entry in ndb_binlog_index +# to get the correct position from. +# We could just re-apply the last epoch applied, but that's imprecise, +# and causes us to create an ndb_apply_status entry for Server 3 when +# it has not really been master for those changes. +# So we just look at the Master status instead. +# +#--echo Get max applied epochs from a server on each cluster +#--connection slave +#let $max_applied_cluster_a_epoch = query_get_value("SELECT MAX(epoch) AS epoch FROM mysql.ndb_apply_status WHERE server_id IN (1,3)", epoch, 1); +#--connection master +#let $max_applied_cluster_b_epoch = query_get_value("SELECT MAX(epoch) AS epoch FROM mysql.ndb_apply_status WHERE server_id IN (2,4)", epoch, 1); +# +#--echo Get corresponding Binlog filename + pos from new Master servers +#--connection master1 +#eval select * from mysql.ndb_binlog_index where epoch > $max_applied_cluster_a_epoch ; +#let $cluster_a_master_log_file = query_get_value("SELECT SUBSTRING_INDEX(File, '/', -1) as File from mysql.ndb_binlog_index WHERE epoch >= $max_applied_cluster_a_epoch", File, 1); +#let $cluster_a_master_log_pos = query_get_value("SELECT Position from mysql.ndb_binlog_index WHERE epoch >= $max_applied_cluster_a_epoch", Position, 1); +#--connection slave +#eval select * from mysql.ndb_binlog_index where epoch > $max_applied_cluster_b_epoch; +#let $cluster_b_master_log_file = query_get_value("SELECT SUBSTRING_INDEX(File, '/', -1) as File from mysql.ndb_binlog_index WHERE epoch >= $max_applied_cluster_b_epoch", File, 1); +#let $cluster_b_master_log_pos = query_get_value("SELECT Position from mysql.ndb_binlog_index WHERE epoch >= $max_applied_cluster_b_epoch", Position, 1); +#--echo Now change new Slave servers to new Master file + pos +#--connection master +#--echo Changing master to $cluster_b_master_log_file, $cluster_b_master_log_pos +#eval CHANGE MASTER TO MASTER_LOG_FILE="$cluster_b_master_log_file", MASTER_LOG_POS=$cluster_b_master_log_pos; +#--connection slave1 +#--echo Changing master to $cluster_a_master_log_file, $cluster_a_master_log_pos +#eval CHANGE MASTER TO MASTER_LOG_FILE="$cluster_a_master_log_file", MASTER_LOG_POS=$cluster_a_master_log_pos; + +--echo Get current master status on Cluster A new master (next pos in Binlog) +--connection master1 +--echo Master1 (3) +--echo Flush logs to ensure any pending update (e.g. reflected apply_status write row) +--echo is skipped over. +flush logs; +let $cluster_a_master_log_file = query_get_value("SHOW MASTER STATUS", "File", 1); +let $cluster_a_master_log_pos = query_get_value("SHOW MASTER STATUS", "Position", 1); +--echo Setup slave on Cluster B to use it +--connection slave1 +--echo Slave1 (4) +--disable_query_log +eval CHANGE MASTER TO MASTER_LOG_FILE="$cluster_a_master_log_file", MASTER_LOG_POS=$cluster_a_master_log_pos; +--enable_query_log + +--echo Get current master status on Cluster B new master (next pos in Binlog) +--connection slave +--echo Slave (2) +--echo Flush logs to ensure any pending update (e.g. reflected apply_status write row) +--echo is skipped over. +flush logs; +let $cluster_b_master_log_file = query_get_value("SHOW MASTER STATUS", "File", 1); +let $cluster_b_master_log_pos = query_get_value("SHOW MASTER STATUS", "Position", 1); +--echo Setup slave on Cluster A to use it +--connection master +--echo Master (1) +--disable_query_log +eval CHANGE MASTER TO MASTER_LOG_FILE="$cluster_b_master_log_file", MASTER_LOG_POS=$cluster_b_master_log_pos; +--enable_query_log + +--connection master +--echo Master (1) +--echo Show that Cluster A Slave server (old master) has no Max replicated epoch before receiving data +select variable_name, variable_value from information_schema.global_status + where variable_name='Ndb_slave_max_replicated_epoch'; + +--connection master1 +--echo Master1 (3) +--echo Cluster A Master server (old slave) has old Max replicated epoch +select + count(1) + from + information_schema.global_status, + mysql.ndb_apply_status + where + server_id = 1 + and + variable_name='Ndb_slave_max_replicated_epoch' + and + variable_value = epoch; + +--echo Now start slaves up +--source include/rpl_start_slaves.inc + +--echo Show that applying something from Cluster B causes the +--echo old Max Rep Epoch to be loaded from ndb_apply_status +--echo There is no new Max Rep Epoch from Cluster A as it has not changed +--echo anything yet + +--connection slave +--echo Slave (2) +insert into test.t1 values (3, "From the Sea"); + +--echo Allow to propagate to Cluster A +--sync_slave_with_master master + +--connection master +--echo Master (1) +--echo New Slave server on Cluster A has loaded old Max-Replicated-Epoch +select server_id from mysql.ndb_apply_status order by server_id; +select + count(1) + from + information_schema.global_status, + mysql.ndb_apply_status + where + server_id = 1 + and + variable_name='Ndb_slave_max_replicated_epoch' + and + variable_value = epoch; + +--echo Now make a new Cluster A change and see that the Max Replicated Epoch advances +--echo once it has propagated + +--connection master1 +--echo Master1 (3) +insert into test.t1 values (4, "Brooke"); + +--echo Propagate to Cluster B +--sync_slave_with_master slave1 + +--echo Make change on Cluster B to allow waiting for reverse propagation +--connection slave +--echo Slave (2) +insert into test.t1 values (5, "Rupert"); + +--echo Wait for propagation back to Cluster A +--sync_slave_with_master master + +--connection master +--echo Master (1) +--echo Show that Cluster A now has 2 different server_id entries in ndb_apply_status +--echo Those from the new master (server_id 3) are highest. +select server_id from mysql.ndb_apply_status order by server_id; +select + count(1) + from + information_schema.global_status, + mysql.ndb_apply_status + where + server_id = 3 + and + variable_name='Ndb_slave_max_replicated_epoch' + and + variable_value = epoch; + +let $max_epoch = query_get_value("select max(epoch) as epoch from mysql.ndb_apply_status where server_id in (1,3)","epoch", 1); +--disable_query_log +# We have to constrain the search to master server ids 1,3 in case the +# Slave happens to have similar epoch values +eval select server_id as local_server_with_max_epoch from mysql.ndb_apply_status where epoch=$max_epoch and server_id in (1,3); +--enable_query_log + +--echo Done + +--connection master1 +drop table t1; +--sync_slave_with_master slave1 + +--source suite/ndb_rpl/ndb_master-slave_2ch_end.inc + === added file 'mysql-test/suite/ndb_rpl/t/ndb_rpl_init_rep_status.test' --- a/mysql-test/suite/ndb_rpl/t/ndb_rpl_init_rep_status.test 1970-01-01 00:00:00 +0000 +++ b/mysql-test/suite/ndb_rpl/t/ndb_rpl_init_rep_status.test 2011-06-29 23:28:01 +0000 @@ -0,0 +1,89 @@ +--source include/have_ndb.inc +--source include/have_binlog_format_mixed_or_row.inc +--source suite/ndb_rpl/ndb_master-slave.inc + +# Test Slave initialisation of Ndb_slave_max_replicated_epoch status var + +--connection slave +reset master; +stop slave; + +--connection master +--echo Generate something in the Masters Binlog +use test; +create table t1 (a int primary key, b int) engine=ndb; + +insert into t1 values (1,1); + +--connection slave +--echo Initial state +select * from mysql.ndb_apply_status; +select variable_value from information_schema.global_status + where variable_name like '%Ndb_slave_max_replicated_epoch%'; +select @slave_server_id:=(variable_value+0) from information_schema.global_variables + where variable_name like 'server_id'; + +--echo Default, no data, max replicated epoch will be 0. +reset slave; +start slave; +--connection master +--sync_slave_with_master +--connection slave +--replace_column 3 # 4 # 5 # +select server_id from mysql.ndb_apply_status order by server_id; +select variable_value from information_schema.global_status + where variable_name like 'Ndb_slave_max_replicated_epoch'; + +--echo Default, load of own serverid from ndb_apply_status, should be 111 +drop table test.t1; +stop slave; +reset slave; +insert into mysql.ndb_apply_status values (@slave_server_id, 111, 'Fictional log', 222, 333); +start slave; +--connection master +--sync_slave_with_master +--connection slave +--replace_column 3 # 4 # 5 # +select server_id from mysql.ndb_apply_status order by server_id; +select variable_value from information_schema.global_status + where variable_name like 'Ndb_slave_max_replicated_epoch'; + +drop table test.t1; + +--echo Check that reset slave resets Ndb_slave_max_replicated_epoch +stop slave; +select variable_value from information_schema.global_status + where variable_name like 'Ndb_slave_max_replicated_epoch'; +reset slave; +select variable_value from information_schema.global_status + where variable_name like 'Ndb_slave_max_replicated_epoch'; + +--echo Multiple-channel, load highest of configured serverids, should be 222 +set @other_local_server_id=@slave_server_id+1; +set @other_remote_server_id=@slave_server_id+2; +insert into mysql.ndb_apply_status values (@slave_server_id, 111, 'Fictional log', 222, 333); +insert into mysql.ndb_apply_status values (@other_local_server_id, 222, 'Fictional log', 222, 333); +insert into mysql.ndb_apply_status values (@other_remote_server_id, 444, 'Fictional log', 222, 333); + +let $local_server_ids = `select @other_local_server_id`; + +--eval CHANGE MASTER TO IGNORE_SERVER_IDS=($local_server_ids); +start slave; +--connection master +--sync_slave_with_master +--connection slave +--replace_column 3 # 4 # 5 # +select server_id from mysql.ndb_apply_status order by server_id; +select variable_value from information_schema.global_status + where variable_name like 'Ndb_slave_max_replicated_epoch'; + +# Clean up +stop slave; +CHANGE MASTER TO IGNORE_SERVER_IDS= (); +start slave; +--connection master +drop table test.t1; +sync_slave_with_master; + +--source include/rpl_end.inc + === modified file 'sql/Makefile.am' --- a/sql/Makefile.am 2011-06-15 10:37:56 +0000 +++ b/sql/Makefile.am 2011-06-29 23:28:01 +0000 @@ -63,6 +63,7 @@ noinst_HEADERS = item.h item_func.h item ha_ndbcluster_lock_ext.h ha_ndbinfo.h \ ha_ndbcluster_glue.h \ ha_ndb_index_stat.h \ + ndb_mi.h \ ha_partition.h rpl_constants.h \ debug_sync.h \ opt_range.h protocol.h rpl_tblmap.h rpl_utility.h \ @@ -138,7 +139,8 @@ libndb_la_SOURCES= ha_ndbcluster.cc \ ha_ndbcluster_connection.cc \ ha_ndbcluster_cond.cc \ ha_ndb_index_stat.cc \ - ha_ndbinfo.cc + ha_ndbinfo.cc \ + ndb_mi.cc gen_lex_hash_SOURCES = gen_lex_hash.cc gen_lex_hash_LDFLAGS = @NOINST_LDFLAGS@ === modified file 'sql/ha_ndbcluster.cc' --- a/sql/ha_ndbcluster.cc 2011-06-23 12:19:32 +0000 +++ b/sql/ha_ndbcluster.cc 2011-06-29 23:28:01 +0000 @@ -30,8 +30,6 @@ #include "ha_ndbcluster_glue.h" -#include "rpl_mi.h" - #ifdef WITH_NDBCLUSTER_STORAGE_ENGINE #include "ha_ndbcluster.h" #include @@ -46,6 +44,7 @@ #include #include +#include "ndb_mi.h" #ifdef ndb_dynamite #undef assert @@ -463,7 +462,11 @@ update_slave_api_stats(Ndb* ndb) st_ndb_slave_state g_ndb_slave_state; st_ndb_slave_state::st_ndb_slave_state() - : current_conflict_defined_op_count(0) + : current_conflict_defined_op_count(0), + current_master_server_epoch(0), + current_max_rep_epoch(0), + max_rep_epoch(0), + sql_run_id(~Uint32(0)) { memset(current_violation_count, 0, sizeof(current_violation_count)); memset(total_violation_count, 0, sizeof(total_violation_count)); @@ -475,6 +478,7 @@ st_ndb_slave_state::atTransactionAbort() /* Reset current-transaction counters + state */ memset(current_violation_count, 0, sizeof(current_violation_count)); current_conflict_defined_op_count = 0; + current_max_rep_epoch = 0; } void @@ -489,8 +493,195 @@ st_ndb_slave_state::atTransactionCommit( current_violation_count[i] = 0; } current_conflict_defined_op_count = 0; + if (current_max_rep_epoch > max_rep_epoch) + { + DBUG_PRINT("info", ("Max replicated epoch increases from %llu to %llu", + max_rep_epoch, + current_max_rep_epoch)); + + max_rep_epoch = current_max_rep_epoch; + } + current_max_rep_epoch = 0; } +void +st_ndb_slave_state::atApplyStatusWrite(Uint32 master_server_id, + Uint32 row_server_id, + Uint64 row_epoch, + bool is_row_server_id_local) +{ + if (row_server_id == master_server_id) + { + /* + WRITE_ROW to ndb_apply_status injected by MySQLD + immediately upstream of us. + Record epoch + */ + current_master_server_epoch = row_epoch; + assert(! is_row_server_id_local); + } + else if (is_row_server_id_local) + { + DBUG_PRINT("info", ("Recording application of local server %u epoch %llu " + " which is %s.", + row_server_id, row_epoch, + (row_epoch > g_ndb_slave_state.current_max_rep_epoch)? + " new highest." : " older than previously applied")); + if (row_epoch > current_max_rep_epoch) + { + /* + Store new highest epoch in thdvar. If we commit successfully + then this can become the new global max + */ + current_max_rep_epoch = row_epoch; + } + } +} + +void +st_ndb_slave_state::atResetSlave() +{ + /* Reset the Maximum replicated epoch vars + * on slave reset + * No need to touch the sql_run_id as that + * will increment if the slave is started + * again. + */ + current_max_rep_epoch = 0; + max_rep_epoch = 0; +} + +static int check_slave_state(THD* thd) +{ + DBUG_ENTER("check_slave_state"); + +#ifdef HAVE_NDB_BINLOG + if (!thd->slave_thread) + DBUG_RETURN(0); + + const Uint32 runId = ndb_mi_get_slave_run_id(); + DBUG_PRINT("info", ("Slave SQL thread run id is %u", + runId)); + if (unlikely(runId != g_ndb_slave_state.sql_run_id)) + { + DBUG_PRINT("info", ("Slave run id changed from %u, " + "treating as Slave restart", + g_ndb_slave_state.sql_run_id)); + g_ndb_slave_state.sql_run_id = runId; + + /* Always try to load the Max Replicated Epoch info + * first. + * Could be made optional if it's a problem + */ + { + /* + Load highest replicated epoch from a local + MySQLD from the cluster. + */ + DBUG_PRINT("info", ("Loading applied epoch information from %s", + NDB_APPLY_TABLE)); + NdbError ndb_error; + Uint64 highestAppliedEpoch = 0; + do + { + Ndb* ndb= check_ndb_in_thd(thd); + NDBDICT* dict= ndb->getDictionary(); + NdbTransaction* trans= NULL; + ndb->setDatabaseName(NDB_REP_DB); + Ndb_table_guard ndbtab_g(dict, NDB_APPLY_TABLE); + + const NDBTAB* ndbtab= ndbtab_g.get_table(); + if (unlikely(ndbtab == NULL)) + { + ndb_error = dict->getNdbError(); + break; + } + + trans= ndb->startTransaction(); + if (unlikely(trans == NULL)) + { + ndb_error = ndb->getNdbError(); + break; + } + + do + { + NdbScanOperation* sop = trans->getNdbScanOperation(ndbtab); + if (unlikely(sop == NULL)) + { + ndb_error = trans->getNdbError(); + break; + } + + const Uint32 server_id_col_num = 0; + const Uint32 epoch_col_num = 1; + NdbRecAttr* server_id_ra; + NdbRecAttr* epoch_ra; + + if (unlikely((sop->readTuples(NdbOperation::LM_CommittedRead) != 0) || + ((server_id_ra = sop->getValue(server_id_col_num)) == NULL) || + ((epoch_ra = sop->getValue(epoch_col_num)) == NULL))) + { + ndb_error = sop->getNdbError(); + break; + } + + if (trans->execute(NdbTransaction::Commit)) + { + ndb_error = trans->getNdbError(); + break; + } + + int rc = 0; + while (0 == (rc= sop->nextResult(true))) + { + Uint32 serverid = server_id_ra->u_32_value(); + Uint64 epoch = epoch_ra->u_64_value(); + + if ((serverid == ::server_id) || + (ndb_mi_get_ignore_server_id(serverid))) + { + highestAppliedEpoch = MAX(epoch, highestAppliedEpoch); + } + } + + if (rc != 1) + { + ndb_error = sop->getNdbError(); + break; + } + } while (0); + + trans->close(); + } while(0); + + if (ndb_error.code != 0) + { + sql_print_warning("NDB Slave : Could not determine maximum replicated epoch from %s.%s " + "at Slave start, error %u %s", + NDB_REP_DB, + NDB_APPLY_TABLE, + ndb_error.code, ndb_error.message); + } + + /* + Set Global status variable to the Highest Applied Epoch from + the Cluster DB. + If none was found, this will be zero. + */ + g_ndb_slave_state.max_rep_epoch = highestAppliedEpoch; + sql_print_information("NDB Slave : MaxReplicatedEpoch set to %llu (%u/%u) at Slave start", + g_ndb_slave_state.max_rep_epoch, + (Uint32)(g_ndb_slave_state.max_rep_epoch >> 32), + (Uint32)(g_ndb_slave_state.max_rep_epoch & 0xffffffff)); + } // Load highest replicated epoch + } // New Slave SQL thread run id +#endif + + DBUG_RETURN(0); +} + + static int update_status_variables(Thd_ndb *thd_ndb, st_ndb_status *ns, Ndb_cluster_connection *c) @@ -620,6 +811,7 @@ SHOW_VAR ndb_status_injector_variables[] SHOW_VAR ndb_status_slave_variables[]= { NDBAPI_COUNTERS("_slave", &g_slave_api_client_stats), + {"slave_max_replicated_epoch", (char*) &g_ndb_slave_state.max_rep_epoch, SHOW_LONGLONG}, {NullS, NullS, SHOW_LONG} }; @@ -712,7 +904,7 @@ static int ndb_to_mysql_error(const NdbE } #ifdef HAVE_NDB_BINLOG -extern Master_info *active_mi; + /* Write conflicting row to exceptions table. */ static int write_conflict_row(NDB_SHARE *share, NdbTransaction *trans, @@ -740,8 +932,8 @@ static int write_conflict_row(NDB_SHARE } { uint32 server_id= (uint32)::server_id; - uint32 master_server_id= (uint32)active_mi->master_id; - uint64 master_epoch= (uint64)active_mi->master_epoch; + uint32 master_server_id= (uint32) ndb_mi_get_master_server_id(); + uint64 master_epoch= (uint64) g_ndb_slave_state.current_master_server_epoch; uint32 count= (uint32)++(cfn_share->m_count); if (ex_op->setValue((Uint32)0, (const char *)&(server_id)) || ex_op->setValue((Uint32)1, (const char *)&(master_server_id)) || @@ -3914,7 +4106,7 @@ bool ha_ndbcluster::isManualBinlogExec(T #ifndef EMBEDDED_LIBRARY return thd ? ( thd->rli_fake? - thd->rli_fake->get_flag(Relay_log_info::IN_STMT) : false) + ndb_mi_get_in_relay_log_statement(thd->rli_fake) : false) : false; #else /* For Embedded library, we can't determine if we're @@ -4170,21 +4362,38 @@ handle_conflict_op_error(Thd_ndb* thd_nd #endif /* HAVE_NDB_BINLOG */ +#ifdef HAVE_NDB_BINLOG +/* + is_serverid_local +*/ +static bool is_serverid_local(Uint32 serverid) +{ + /* + If it's not our serverid, check the + IGNORE_SERVER_IDS setting to check if + it's local. + */ + return ((serverid == ::server_id) || + ndb_mi_get_ignore_server_id(serverid)); +} +#endif + int ha_ndbcluster::write_row(uchar *record) { DBUG_ENTER("ha_ndbcluster::write_row"); #ifdef HAVE_NDB_BINLOG if (m_share == ndb_apply_status_share && table->in_use->slave_thread) { - uint32 sid, master_server_id= active_mi->master_id; - memcpy(&sid, table->field[0]->ptr + (record - table->record[0]), sizeof(sid)); - if (sid == master_server_id) - { - uint64 master_epoch; - memcpy(&master_epoch, table->field[1]->ptr + (record - table->record[0]), - sizeof(master_epoch)); - active_mi->master_epoch= master_epoch; - } + uint32 row_server_id, master_server_id= ndb_mi_get_master_server_id(); + uint64 row_epoch; + memcpy(&row_server_id, table->field[0]->ptr + (record - table->record[0]), + sizeof(row_server_id)); + memcpy(&row_epoch, table->field[1]->ptr + (record - table->record[0]), + sizeof(row_epoch)); + g_ndb_slave_state.atApplyStatusWrite(master_server_id, + row_server_id, + row_epoch, + is_serverid_local(row_server_id)); } #endif /* HAVE_NDB_BINLOG */ DBUG_RETURN(ndb_write_row(record, FALSE, FALSE)); @@ -4208,6 +4417,10 @@ int ha_ndbcluster::ndb_write_row(uchar * Uint32 num_sets= 0; DBUG_ENTER("ha_ndbcluster::ndb_write_row"); + error = check_slave_state(thd); + if (unlikely(error)) + DBUG_RETURN(error); + has_auto_increment= (table->next_number_field && record == table->record[0]); if (has_auto_increment && table_share->primary_key != MAX_KEY) @@ -4752,6 +4965,11 @@ int ha_ndbcluster::ndb_update_row(const DBUG_ENTER("ndb_update_row"); DBUG_ASSERT(trans); + + error = check_slave_state(thd); + if (unlikely(error)) + DBUG_RETURN(error); + /* * If IGNORE the ignore constraint violations on primary and unique keys, * but check that it is not part of INSERT ... ON DUPLICATE KEY UPDATE @@ -5086,6 +5304,10 @@ int ha_ndbcluster::ndb_delete_row(const DBUG_ENTER("ndb_delete_row"); DBUG_ASSERT(trans); + error = check_slave_state(thd); + if (unlikely(error)) + DBUG_RETURN(error); + ha_statistic_increment(&SSV::ha_delete_count); m_rows_changed++; @@ -6570,30 +6792,15 @@ static int ndbcluster_update_apply_statu r|= op->setValue(1u, (Uint64)0); DBUG_ASSERT(r == 0); } -#if MYSQL_VERSION_ID < 50600 - const char* group_master_log_name = - active_mi->rli.group_master_log_name; - const Uint64 group_master_log_pos = - (Uint64)active_mi->rli.group_master_log_pos; - const Uint64 future_event_relay_log_pos = - (Uint64)active_mi->rli.future_event_relay_log_pos; - const Uint64 group_relay_log_pos = - (Uint64)active_mi->rli.group_relay_log_pos; -#else - /* - - Master_info's rli member returns Relay_log_info* - - Relay_log_info members are protected and must be accessed - using accessor functions - */ const char* group_master_log_name = - active_mi->rli->get_group_master_log_name(); + ndb_mi_get_group_master_log_name(); const Uint64 group_master_log_pos = - (Uint64)active_mi->rli->get_group_master_log_pos(); + ndb_mi_get_group_master_log_pos(); const Uint64 future_event_relay_log_pos = - (Uint64)active_mi->rli->get_future_event_relay_log_pos(); + ndb_mi_get_future_event_relay_log_pos(); const Uint64 group_relay_log_pos = - (Uint64)active_mi->rli->get_group_relay_log_pos(); -#endif + ndb_mi_get_group_relay_log_pos(); + // log_name char tmp_buf[FN_REFLEN]; ndb_pack_varchar(ndbtab->getColumn(2u), tmp_buf, === modified file 'sql/ha_ndbcluster.h' --- a/sql/ha_ndbcluster.h 2011-06-23 12:19:32 +0000 +++ b/sql/ha_ndbcluster.h 2011-06-29 23:28:01 +0000 @@ -344,13 +344,23 @@ struct st_ndb_slave_state /* Counter values for current slave transaction */ Uint32 current_conflict_defined_op_count; Uint32 current_violation_count[CFT_NUMBER_OF_CFTS]; + Uint64 current_master_server_epoch; + Uint64 current_max_rep_epoch; /* Cumulative counter values */ Uint64 total_violation_count[CFT_NUMBER_OF_CFTS]; + Uint64 max_rep_epoch; + Uint32 sql_run_id; /* Methods */ void atTransactionCommit(); void atTransactionAbort(); + void atResetSlave(); + + void atApplyStatusWrite(Uint32 master_server_id, + Uint32 row_server_id, + Uint64 row_epoch, + bool is_row_server_id_local); st_ndb_slave_state(); }; === modified file 'sql/ha_ndbcluster_binlog.cc' --- a/sql/ha_ndbcluster_binlog.cc 2011-06-16 18:16:01 +0000 +++ b/sql/ha_ndbcluster_binlog.cc 2011-06-29 23:28:01 +0000 @@ -47,6 +47,7 @@ extern my_bool opt_ndb_log_updated_only; extern my_bool opt_ndb_log_binlog_index; extern my_bool opt_ndb_log_apply_status; extern ulong opt_ndb_extra_logging; +extern st_ndb_slave_state g_ndb_slave_state; bool ndb_log_empty_epochs(void); @@ -892,6 +893,8 @@ static void ndbcluster_reset_slave(THD * thd_stmt_da(thd)->reset_diagnostics_area(); } + g_ndb_slave_state.atResetSlave(); + DBUG_VOID_RETURN; } === added file 'sql/ndb_mi.cc' --- a/sql/ndb_mi.cc 1970-01-01 00:00:00 +0000 +++ b/sql/ndb_mi.cc 2011-06-29 23:28:01 +0000 @@ -0,0 +1,86 @@ +/* + 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "ndb_mi.h" +#include "my_sys.h" +#include "hash.h" +#include "rpl_mi.h" + +#ifdef HAVE_NDB_BINLOG + +extern Master_info *active_mi; + + +uint32 ndb_mi_get_master_server_id() +{ + return (uint32) active_mi->master_id; +} + +const char* ndb_mi_get_group_master_log_name() +{ +#if MYSQL_VERSION_ID < 50600 + return active_mi->rli.group_master_log_name; +#else + return active_mi->rli->get_group_master_log_name(); +#endif +} + +uint64 ndb_mi_get_group_master_log_pos() +{ +#if MYSQL_VERSION_ID < 50600 + return (uint64) active_mi->rli.group_master_log_pos; +#else + return (uint64) active_mi->rli->get_group_master_log_pos(); +#endif +} + +uint64 ndb_mi_get_future_event_relay_log_pos() +{ +#if MYSQL_VERSION_ID < 50600 + return (uint64) active_mi->rli.future_event_relay_log_pos; +#else + return (uint64) active_mi->rli->get_future_event_relay_log_pos(); +#endif +} + +uint64 ndb_mi_get_group_relay_log_pos() +{ +#if MYSQL_VERSION_ID < 50600 + return (uint64) active_mi->rli.group_relay_log_pos; +#else + return (uint64) active_mi->rli->get_group_relay_log_pos(); +#endif +} + +bool ndb_mi_get_ignore_server_id(uint32 server_id) +{ + return (active_mi->shall_ignore_server_id(server_id) != 0); +} + +uint32 ndb_mi_get_slave_run_id() +{ + return active_mi->rli.slave_run_id; +} + +bool ndb_mi_get_in_relay_log_statement(Relay_log_info* rli) +{ + return (rli->get_flag(Relay_log_info::IN_STMT) != 0); +} + +/* #ifdef HAVE_NDB_BINLOG */ + +#endif === added file 'sql/ndb_mi.h' --- a/sql/ndb_mi.h 1970-01-01 00:00:00 +0000 +++ b/sql/ndb_mi.h 2011-06-29 23:28:01 +0000 @@ -0,0 +1,47 @@ +/* + 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef NDB_MI_H +#define NDB_MI_H + +#include + +/* + This file defines methods for interacting with the + Master Info structure on a Slave MySQLD. + These methods are only valid when running in an + active slave thread. +*/ + +/* + Accessors +*/ +uint32 ndb_mi_get_master_server_id(); +const char* ndb_mi_get_group_master_log_name(); +uint64 ndb_mi_get_group_master_log_pos(); +uint64 ndb_mi_get_future_event_relay_log_pos(); +uint64 ndb_mi_get_group_relay_log_pos(); +bool ndb_mi_get_ignore_server_id(uint32 server_id); +uint32 ndb_mi_get_slave_run_id(); + +/* + Relay log info related functions +*/ +bool ndb_mi_get_in_relay_log_statement(class Relay_log_info* rli); + +// #ifndef NDB_MI_H +#endif === modified file 'storage/ndb/CMakeLists.txt' --- a/storage/ndb/CMakeLists.txt 2011-06-15 10:37:56 +0000 +++ b/storage/ndb/CMakeLists.txt 2011-06-29 23:28:01 +0000 @@ -147,7 +147,8 @@ SET(NDBCLUSTER_SOURCES ../../sql/ha_ndbcluster_connection.cc ../../sql/ha_ndbcluster_binlog.cc ../../sql/ha_ndb_index_stat.cc - ../../sql/ha_ndbinfo.cc) + ../../sql/ha_ndbinfo.cc + ../../sql/ndb_mi.cc) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/storage/ndb/include) IF(EXISTS ${CMAKE_SOURCE_DIR}/storage/mysql_storage_engine.cmake) --===============6801600364784646883== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/frazer.clement@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: frazer.clement@stripped\ # qis51pg6aod85zyk # target_branch: file:///home/frazer/bzr/mysql-5.1-telco-7.0/ # testament_sha1: b51cdce5840c9704fe45769c5140cad7d29dd2bf # timestamp: 2011-06-30 00:28:10 +0100 # base_revision_id: jonas.oreland@stripped\ # hqlx03cb11mcnd9a # # Begin bundle IyBCYXphYXIgcmV2aXNpb24gYnVuZGxlIHY0CiMKQlpoOTFBWSZTWXXOtwEAF6H/gH/0HUB///// /+f/7v////9gMN72fe66aBt717XIrVGhoKDStE84Dttd069e3d3bXG7UNseg0AegB4cuzVi7YTrV 7GKFVclTGaD21XbrId3c21e9jvbk4UzBUtjGSsKtamY2szZlratFQbNZtu3Vcdd3bp2bWjMxasAt bbQyEp6GiMTRqaelDJ6T1Mm01HqM0mj1GgAAAaAAA0BKEAEAgTIAJNMmRonqnqDeqNAGg9RoABtQ PU9QHAANBoaDQAaZBoZA00AABkAGQGQAEmoiITTRoBBqnqeQekaT0pmU8RGninpGn6hpD1BtRp6g ZAAiUjRNNEyaYITEMkT2k09TKfpGgnpND0mjRpptT1GmgANGgKkiAiYTQCNCbSYRkyJiepPVPGKn 6mKabUZpMnqGQDIepnCID6oIBmMx6/hD+v7LbZ608P6vXfMe0+6HjgUhFBX1ExX8DrrSfg9F0c9f 7fa10c9f74qTKvMhlqlggDJohQ1D79IQLI4uoMQht72fda5LzbhRrjIoDK19FG5gV5R2VmufTD8/ kPs1FKrS2jUvz92sdN0kIQjKz0IDrcN6nabJl/MNPA+TxLzrNl7qPxPOW4/fi9I37Lnl13r8vOH/ a5bitRVV251rsSr/EzqKPJjI7D8XlKETu2fus2s1BaP/b1g6s6zNQ0oM3WmU/P6Sw52lAOrh7G1y OIcRWcuQn/TyhuNykuft1PSzbr3aSq5TyKsOAYnppgR48c0oFtIMbx1SiLLj0yTMBPIJ8fctUyEe D/bSCvxVz+xx7n268YqcnejsFX3rg1x30qGtXXeEyeW4IG5fEgzLII3/jNq+pcp+apEoDbuDOUZd 9kbXfppMA5oKKowlQp4QvCBkorMcHIV0ZIPlJI+EEU7/o5w/C0R1EzT+UtThFPFTLFLIMCsZo5W8 jc5veL8HMzDmGC9FRX6sR8aweHpjtREi0ic3pD72aGvLdBTRYaP1cQDR+ejh6zrZJGS36rNS3Uqr ObBZM8XQX0VA1VERHCVBQYAlVmcpy7PMw+Mo+tA6wkGwRwIR14dr6NzLdHOdBYtGEN76rKT1ZZKy LPh0DZrO7XnLD3g4QquoFlGByaS+YKhy4MrRF9YwXXcaZbbRLIHaWQRRQxFHsjk+6sgVFYGeVbHf 5Kk5QwUdAgfe6q7c1OQeTOjaqvecpQbQgwLS5W0G08cCM8Mzgqn85wQQQcihKSoGdpFpKVOmx0Xs WUnJctJUBhLKURhBStKBdY82f7eXR46C3p33vu9zPt7fM1owMzJ61+wVRmJc4+SuGzzbxGaO7QuI KId8bO/CNfXfgmLajM/jkwS6/jFlFE9225lloXlZ4RYdUBrfcUcPFZ6iKwGhUFPcptIsd55TgaB5 z1wnmJ4ERVUAUFkWcA2nN6y5Pr/5qIb+NQLjRt68cJDr09e8wGvoz723Cnwo9PdZgJ2Mx/cXICH0 yxL1W9IK1U5aFMfV54AaYwSKjatjdsR/cmY8FWist3OdYW06OSCpJ5NLfoCFCdpzg6rIy+uz7vku CyUgs1pJkcXabqsT+PTednb2nu/MHk08imD0+ap5CWlY8sp0AorFGDAJUcPPgKfDc8w/CcHPmIXR Jgn+goiH6SJp8D1p9U5CTmfZ6j0PfGk9WeOmEWtZTrfyYJdgsUYLJc6+UsjH7HOShJd6JwFUaB95 xNDYUYhRLnzCgM/8phHN9R05V/3FuDRtb9fMmT8+L4n95Nhc8rzsYhZEbp9Yt3okG6KPMzMHIxEU bqUd0bdi1TAT/DqHR3U61RKC5nibDz1w7xN2/D9pELTpoWBhiRvawHyPXy7NsdSUCa5xHkoA8wBH BvhgIqkkWMf3nQG8wOdZofek8/i38o+Ufbs3JTs8DLFHMxSseXAwChOCxkvJ5RmMhQpT1GkF7Zyz CBbIUZRzia34j5CwGIfvvMQyOscGQrI6WOJjv1KlE1BF0+Qtr9ZDUU6ZHWYj3aL9a1II/Hm5UnrL NcfAz1QtCYtVUHQYHMeP0yT7Ww5lCaLR/ZqQsQ1t0NTpZdS91U7d+x6j53Nka2tNFOup7a7lchs0 bV8NE77Fw4XayCbza0h4drEXDER3fPvk9Lh2N1NmpQV9gQ6iAI8YwHyVWIJgpL6YkX9fO9evhPq3 YzPfB53hQzRRThoHeZl6UcDDW54vNeSi8th6oW/ZMnmOCKCnL5+bxU53gRMDKgEbq8i3oIhAjU9O gdRfJBCmFTxsw9uKjLi4YyP7tzOEyOpncYtz0uDFw6iwa9Juuu6MtLs9wtxGm3ilYUxMCAiMIspI byJGCI54rT58/FNMcKSuQHa91Y5rPEPvOIJYBQT7EEyTwcaKq/wNjed3kRfVpguRYUNQ28cmRbR4 O348xclJJopt5fOquIcTRHMoqiinWxgoxI6zdwyFkSgKamV3EDFigKXGGGB6u1jZ57CQIw6SGJOi CQuH5iSEsWBtqk3kTYbhKOqQwam9yG0+MM6+/ohxqqRTxnQx5ki0xmRepBMi2wiSN5IOXQ2VeSYQ VnbP3kCiZq8wDiwkXN5GqIjTsSBFti1xaRF5hPTH26B8Bk16weQhfYFc5e8yYno00BjEQVSkpIcK ZZoNnNvIUvvLoZm0Hx1MXo4VBwiiZBwfQuwSiY9qJt5rpg9qmxz8MMHCs2x4nlGkxTc4Y3qdhjuZ znwBoNYiNO8Rx6rUcQNo/eVYv1O0UVUInkTM36iYrpD7Hb7qqZGdEXsuqtIlr1pxfUiMVbIMX0N9 Qi/DcpLMsSG+IJJA9ookkmQoUnSUlGWfvcPEu4G9rwrD5vfzZaHqb1pHl+9wZse52Ydk5MToXXcd se1Sf4Un421XiGnBv5N/QqdLqRPdmy95MlqRI45XPh7LmrYRvTUqEaHVCBCiJj4aspBp05qtZ3Xw NXalmqyAPlpTdS6ysjZkTDiDZwNkmw+A2FCCeibyQ7PKUk89MoUQiV/ilp7rAMXBoBEI17LJOn9f uw0iJEAGIEVQifAIZEoQodWBTvPWVJ1Mn1OiJkDu/DgyfABRmZYL9yRZYG4BVVVBk7utlFVU17Kw QT6vzQvXG8/aUVjeeWUsqNu35CkMZti6RupVtevl4TOP0mAoGCCCbAoqShgHoOWiB/wOu5rXjc+f 7jMDKZQkfrLLjqmKeJIgp/nDc4RO73tk0HHCdkYezOTJ8GeFIAulec+2yGvMoFaFAHOtui2L4zx2 csqdAQibM4ZdM6sUKMGBNTvyYVnDqeD8dk3/Pjzv5OJ1dJ1GegmRkP+rlIXEgS1KlcB9hxKKZFRa tP7SJWL+VX337fqo1D+o5fA4TsOGmxtd3Qwrt7telDAnGuD+FHxqOVGEy02mBUa7ZeWvdeXLuFrq ztNxjdJlIPBiVOLU5gFnQUNzw2v4EcybZ4JPWXBxQjSNQfz8p1FTs6FHGsl/FjbDBZTua30OHh4P I1dyn4vI2GSzJTFPi2uxmwjec1MhoLPiwelEmCg1Risq+4JOIEozloPI/IfHaBDYHsq7zIYtm4cH YftdxKpKUKqFKRVKpAkVHYLefh62XHUdZddJcaCh4zhPmS9CprKKZrLpJm+DrTnbWBemCnp5MvJ5 W91vU0vzqp75H1DoXA8mtVXSUfKZ6w+ko/okgShDUeYY9/RxFcwXGMN+sVq5TpynSFqmQbQlEIv4 JXtMiRwFGXKXKHjh9HSfbGHa/zqHeXG9ZCZzCFmx4Pe9Z+Xp1c3l7rq648OxXPxsspUknl42ZHWQ gZQ09BJLJo6uqia7j49vm2YHZ36NzROwL5YmXARMQuTa6dPBkcAbntF2vte0cfudj7S0s717ST5O ndE6KO2rH5LSouWgtGiuXeeTa+iL665YSnRMskpA9SE8+K+Px+9ybM+xTTAgXYKnuNl2xx2I9vJ9 FNODWykk5mxoSTBaxDyHjitxSF6lGjWpQUPekpCZifX92SbHoXESqHEeFDF6CCLiTGNtnjzMapNI o4YsVyMro40TA1MCKIkU2RPwTAvQkSHJLqxMj1GOifARO0yOf0u3PNmBjNMKPLCwGMvllNtVwMAZ DwCphpwz256FwOGYdGJf3FG0h6bEJChoGFI/cTPkoiJuM1jlftqVJnapmbEBBE2meJ1+2CYKhgXE EXdaSBlSwtqlvbkw7rpF7peUO7fUyzltz7bd3EaFZWnLGPQ34EDFJoWNKJhhmiI7eD0g6fFa1YKk D47tB2i3F1N6GZiXhkRGgXJ4ubvFC2vNyUJmuA+2vbIvmSI5FzgeLH5zY8JKHoKnUgmPHceexY0N DkpQfzA6ju4OEJjzeQETVKCImv/vAdcPvxOSt3Dy4x4pnEzekwAiQ2bWvORgmSD03dNTaYZBKkEX SVM+gXEKbHWU5+e4bm2CSLFjVRBqDx1ZwREmOGERHcZFLTobbh78LHUZEyxHyLjz6B40ZwVsSEBE TLIl9RoiIhlzGnCvZe4B3GEzI8N5sTKn4gnA1ImxvCP5DzKKfacQjZ7ZgDQNca4J5QrdqSV2agSp 7MLyGq+GjeuF2zFCOXj5iECJFVRHIxiHd4NY7zCeDAFmwN5hnf2bDz2IiCsSMRigxYooiJ2rggOj NAtQuUfJe8jhfAgGZxOBVEeRxwBdRjIiKMetpdCFiJPOBIYWQP+ZS5EkTLia6jkcRMjzOAehGo4x O4kJsdY5FE3Dxjl7xi5vT2OhwOPHf3OJnKPJoI5H8S/DVhk3CFtmfP0BVeiheUytN49GCu8mi0YU VchVI4jg2oQ9UisRKEX6VGgijzbX0LEosRdRJnM7Cjpgr1GPIeUfI8MXWmyxaF2HGJA7RE58dDCx MjvHky/g82MzyE+JuPmFLnxNxwN/YuyJE3abuGbSSEOlOw1ET+QmPlKdxUGGVhSSF0HnQsZiIlfE 5kkrlxIfm9QjiO2Sbyjzrj19boSGUzockqQoNmWHyMSYbF4wN9gcEiZoMUHh7CBzJkxjIoUO88Oo yERI+zpPe+GW0ILjrBzhysUpN8zMmWBbDFSJkWR9HsETI1xOlBooAnGJwPHEyJlT50BPZYKE87vl YVq36txpkUCRDMzFI6YVyu7d/VKSfOklJNHW23dD6kmrR3uhvdvJZgfYVC501ExPbwoJIwpxu6GM cYy4073tGByBTYaMWL3jJlxGPMSBQx1lTYV+JQMz3TLlJOOBRGMi8B8Tp03HDCRzuiJvIkDuNxIU cbjbI7vUewlXTYqWVXurmryRxHIo6YiJvwOofIsb3Orx4uS5gKMbx5vsUwluNC5IoOMQoUrYxRLF 00KiIkYmkbFhnyLmg+ZAUo7hYcWKuKldCwupoWNR5Y1MoZ5q/DUUjlm8qMOM6OSJAUjBYK+px2mC IW1MEiYkMx1jUxgScNAxSAuTFB5ebGhniaDDixqVG2ncGUujjEqdhuMBd3dc3YhUyGMzcJHRzIHw 614kTVLzUymwt1cppnvbBUciiJDNySgrR1NCTYAbNH8cPNyUHFnyPNVBv9NimnfM2/+ffoxmjn24 17c2IKKa6Xcnve2cvLg0XAVJoMl1QcqzkoOxw2G+pldl2TA3VGBjkPAMZyodJZwmuFs2cw1MWUsq iamcM+uU0lrYF4kLjPSTqOQ+kwLdIeRGSJ7gfkHzY6/qC5GTzQqSJxU1wWst20rkpdFDpj6A2X6W UqIoijDohNlxgioMREk4BwuFnnnBshkYmXp/VExcqnwSorsqEdfQ3hi6VXzGRhDYFA3WGogn1EEf SP1i/QWHuvgPUfaWmQB/0GAXJgFq1xjJJIcC85QPlEB/uaHyzSKCAogotYOAM/YWEnnvQ5j7Q+sM BtkDWVuG5taxGRWSQE7+NQ45B4v2GTND+j9BSLLIH5dhDkDq5TXlVttj/eTgfypvneo66TuLpz4l FLwdzoTQykVIwGPOwtHWxGK8je9CnyKSlKfPJbn3Fk94o6XnWTFSeKjB4zjE65GCd+AwIUQuTVFU x5g5Iklhz+cidULnaG95JET9GcUD8Q7RiBxHAqG1gYuSLnNPQL/1XPzKdH8mkbK6m6MJwnYeCess yTl2nTDwymo4Aeh/d395cJNgIzyUHF6V79bCwVkVYxVQhTJTsec8kMnbj7l5Aqd97Q2G89IThYod Egws7Esm0lGQzxxv9PgTl/eU4vl0D7j2yEzZgRuilVeYJ3B2hOsTgHtHjkMk+ZlRVa6+h6ry/Czs NJR3GrsloxYKeJRpSeeHczZQaKGiZVGMjk2R4PPHJks2MAhBX2dJ/nTn4ZIp4T0lOYKnEYnXnCLx BCizFOEjhY16SsCn2z9BkubBMfmXclJ5nxKqqqqhyMWUyc8aVk5JngeAb07491ua7dOg4FBeh5nZ ToXs3ks2xmYMzMOZUivP0HivJNpyyHAsnfDvENx2jznh9tj9OX/KFDIdhHvqrArJCgC8MImBjBGV OOy30O4bGsnS6DxnrmbxGtiij9n0/v9/6VUq/616tJdU/g+STm8z83zFHpnI8ci6h9HWayjCH/Yu tF4ljL2zrN6T3D5iJZJUqc6w/3k7oPoXE9GA/pqsJnYmf50jsxtx9gll6TTRaCS8w0VVFaqVTikh uJ7PsIb0QwWQnxBLCZ0gHyILICIpFgqyEWQWNxeShSXDdeTPQYVL4SYhtkxjRKQYgKoKiiioqjOE DZHlpURd5KqqoaBqmtoGBkNkJDEm1JSQlm6wUkQQFkD2N2ER1kNA5JjBSQlmSa7MiOm1RVR3zeHM SGA0UaIB7t4LGgmxslFaJJicaLxVli1qpVjAlxlllFhUNJtITWADHhBkn0kL3iVVGRpEb5xLopFI ohQphFFpmnXfrMpIGJD2vZcwYohwOPuYLKk/EDJkwTB+xJOJ/AxdJJyfx7WD+pxf2Pnf1uhyXYPc 0ucxUqWlSED6TUeYXSppMXjOHYOqRgMkiQRON5miiVzG/kdHcqaEEyNynL9nF6hwJD4/HsdruY97 b5msYVlgys73FXleWzI6a6Q6HTIdqvPadpxPA9qnJMH0sVO2ljzT+6NqZ+VDXa1wLKLIhBWyxNJ3 oF1+FhjegvzkBNpl/dLNIT21JDn9mzhW6zWmLLY1/3DT4lij1RDjcQS4vKDRG0b/WK7WtoHhA+VI KlAgwyYd24aWQTTQ8+AXjDnRbVFKWCVxJDuh0Z3DETRKFgMuHSHjeRzc39SbZA8hBIiCIsUiCSJB BGCkkEzW0oGeFEpDpJMX4eNas8A+Xp6C4cTjfuWPOLkU6JOdSSqSHyYSmLbdbRRm8KZsrLS9Sr6t ilLJJs1kRPOZy6RyqQYEIrIEgKQggQgC9h/4v2qRqKiiogphId318wPJu6jn34YE7Xj4xjk9j7ns Zrr/Ss+EnufU+h4eGvCONRIeopFlFkUhJm9/QutAxxmO5jI2ODQnPKjuwxX0iaoSbKesV9/4Ze02 OP735H4t0ZnsjzLB7B5FSPNzCKyS6YWVHwfLLHrxYzxsDkczxO0edZ+Q0qfUODuKnieaes8ukTD0 LkzAzOFjQJqPAdIJyPym8oSb1AI1c52A7zqLDwU/F7H8Xvchzqm5bmaTXkYjFzS7J5FNzlITWKZI nDHLDGCAaxzQwLBSkY36kOsi+ZU0VPCp5aOSSc6Bs+5hqjDjRTbZNbgm5V9WlFyHSOAfcJAglHnI eJyHZL5eqBDfft0VMcqHVCH7WEx+r10oeoQ2EYa/KMIXbnPa5sL/r0knVQilFKk6qIUpWQHsIjdh 0RCiBY1YPz6vd+mDFj3lWuAqMrWSyyyh+7avItUYCnYlpLFosY0vM1IFBGI4OAWXBus0ZcmAMShl Fl1bEbTPh6rLCfMzVKaQNrYMgYENll3vFRkrUxLUet6V3w9bu9XDhk87R8GjBky2LPZ6OWbRvknw Sc5wklLPmSTC7gwbmE1nVQS04SZ9bpXGKkFSVMbHqZO13H3RkfrUFJKiQVAoK5lkOXY4KfQ94x9z X1DLKSlBSicugMTz+Hh2g7amTZULAMh2m71QIAcREYyHWqwP9yTGTL4kI/P7+4DMuKHmNE8PMfgh u8f5e3pDVGbiOCnxstlSGmNc5+I1B6CfWk2MU0xL7fSmLO/MMTZsUkZbaWOs5ja2K/S1axuXa5qv q687fhVtFqlom4yURYYrOUSRBTMCUYAzLAJEChIyETNGM5NllYvejQFiUYsrIYKEOQHRgW7m3nMl 3lc0y3JiYLJoWeJoXb9cxVM9N6kjfVvgzv6F1JfHokN3I079LxKdAtWY10McWY1JyvRqLG+mLmtN qkhTvNqwvE0i0qD1daDGHkBhSmUNsiUYDyRrCA2tqcbqGa89lRzWZBkF0WcRlJnNhxTPRE3nlRsR eQLroDzyc3cwXe5R6Um8MEkWRFrNbpYwihdUpSRgwhFkfF9L0OXUp82yfu22emobd9k+9ScnbNFz xPcgW1Ru1XZUrZJPWpzVhUDsKPozWcSAZ/IXL7KUnqe0QxylZSGUjK8JfieClXjmuCkehCM0WLMW FnZ7eZyNw8iKKoCkGXB5N41d71+7N7PRTuex0J4J3uTzHytWrKZugcpoM2Y5A3hY5OPhmb4KGI9A RXdvM6g4FuV8gdN9QO73RXlaGSrRVoFrmJ0EyhyNxf5A/62jwbx1H7hxPyX7Rc55zoqVUklRJxhd Nll5J7HzuwvIKkpJRUI4cNJalCyKRGIKLBYKIyPrYQPhjAuNVTEWRBShm4IbkkQRQCbWeYJ00YjP NUlQmEROpC0RggzSSRhDDTJAtYoSQUgPbBABBJKHtDomEQ/rzt7ne0SVWka7jgnBKMcVEyIcgrA8 GSQBYfhgHHt492HFBByeY6aK3FYdeQxJmce2E7ljN5vmP0sHYVEAQcZKAjdDoPTmmdGgaZzpCRDR GARiKbzbPvTONW8MUe93uuSEwOMaU9m5E3j77EZDMD1ZLx1SJo+t6zVx8/eaV+dUR3FFrKlvutVl oVVsPfd7kfVrPO+Dazna+p1N6fWze2z7Wx9j7X2NyzFqznFi+xTNqyUePzVc3MadC7au7DoYOZtS Yqn1mDBkGH2730fBPO8c485X+MSyc088TANq8t9L0vQzxjWaHcnFeQ1kMWFx71oAWdR+890WI10V XEIoBr1mY6h8pva+7ScD9Wk0HEUqSqkKFbMboxKt4ZLHeKwV+PB938E/v/qtfkl6rnQOBCKEDzQo Rt6FNBIyMjIrpil+hEuG62or0VSJ72aZZGVrFrWLeZgL5z1J9TCPniRZnaEO0BtIqJC5KKgSUiJE S3IkSyNoMW0F6OwkCd38Ft2e9A+og0gxW+1Wil4UBD4oMgkgEgc915FyEFdJWQSKS4oqUptR+Ety odMKNwFyQkvEKYKu0ks/QlOEoy2OdT1tojFP9UnZBRQqz1JPTq+XLKkTB8ZEjefa1L1VslopLsJa QPK/ePmyvhnK/tH6befspPkWtI0jCy6JLErFAFsFiyAs7GEJUQN+aLMZJD0Fz4Tm3hsQfYnwfn9+ 6GfzNV84WCMwpyWbdz8uolxzxxmKR24ryGhRalikHgPc0jk6v7OE8sqTmUHbpaVvPOe2BOkn+Wge hYLFiifwgGoHHq2huloOusyr3RZ96r1RHjMSzunTP0KdzwpuXPa7bZkqXYrgH6XPAiOYEsHoh63p SEkIkIhJBhAOY1haKe97/kw16XUQQCSQILAUp8RQ5A74v3jxqbu5wG8gSLBjmIFD4RXTYn7r3qcI 6ACWJDx3DrGJ3oh9kpzrvHbU7odKGEaMZNtaNHXE+BZhJN3JrLq6n078DLyJD0C+jpOvctFH8ZTZ hwn5tg1HoCh8QsHYpklTZTAbji9YdJ2HU4ul1N21J2RWdFNyoCFBUJ0mDtktFkIb42GpyHrGTua4 mchahsNBrC5RJRSprlOO2Jr4S1wzKedGAQCESRiECCi2ArpkKVStpdf2ayarWCgpKqSo2rNgoRHI ttEYgp8F6g5kssvvVbjRUpb2eCSSQhPY1o1LhuUAyuqgp5ziF3AcpkNTHOXtAT9Ry1REOv3QamsN UPX7GSSu9Eiw/WbrsnAKgJOkGyCQZ87CBiB442rT4u63lR3+1xnSqNOSPKmdlGsSYSEbZtPEYeVC UkOzWaN1dvj3dGFhc71RJOfeYVDiok4lSSVaWjC91LiNzt3PLix0FdYfHx0OMynBuS0N7FCMIwCX wohIT6KAhU9aaQgMDygYR+ZLfkuhabT6cFLIoE4N542hqRNUR3EFeu/EuRC3L7erAM1vwmrcRL8b l30vBMfsankmqbDeb9yT+jVvSsLPzLz4bX5/u1/zdR8hvQf1Hnzjfs404b1QaO2UFEC8VYpcamaq TpjDNtEigUYZRUvsLJNMkwILZlIFJ7QwDAahkgllWCstaCb0pAXOjAChYNqLsM32IwWMJJNLClIX WyLmAeiWl8DV+FcjbP8C/kKSkoUkUo5dC9yqfqfkPc+zJt9M/FurDXgzW6x5UUUXG6g/MybeiGwN irhlKzECYE1Mz3DpOwDQDtQ7KLEQ9kkKM17pcxFYEebU+fxNEyjvqlCBQjlVpDAIQ3489y3kf0Mv OACiJhYRYUl98H0RUnjKfIunsYtqfv1mvCkUNohf4khTYaiWyMEKAGUKARhISIkSyClEkFKm0LHx FiYQm47D0UPkoFUSiMQYE6xgSgUpiMEEuyrYRD6JQ8NpYBWlKEV0RKkfRavJloaDA8Q2fa/1qFZW gVRH5yEcUk1mbey8UG1zBoMoryMFW4Yj2h271MwbsfbgdIThSrlRQioSGYJ75PIYJeCRJFEkQNxD jIiudLc15j8xyV/FVzQ1NtFQysRs0E+BlLcXAWFk815cO1Vo5Nmra72SMFpOeeIIWxYDtI5JieYU MVDFMoM7hZg8SU9UyduMQzc/ZnKIcp0f8l6K/a5pNxvhyz5P+LbtOum+1ulx+8xQuh0cpHA7tGEd ODeWTBLFlM0ku/25NrN7JzvRFvlUXDWKTq3ofzuDDRIzQtLQ22D9NolSSsI0SYP2zxRkZFv7Zmy9 afikn49X7XOkN84OGy4HNQwsYBCofsVEEixQgiCowUkZCEUGRUkAMyarenTBIkUIYBrfTFgRGMEX HIYrcl5BTirXT5yp7ouDyKfa7W9J4hk6nO/QknjdMkHlnxe59TlNDwqbBh7xlMvIccF1wIRE9glE IoMJAGEthUYwQKoKFO5WBSCxIhYyiYuLbZUNrC0gFjFhD+sGusqqTBFJJIsoMU8F0hepDOyc6c/q YAfQ94uDPK+BV7eKVU4HQpnaRXIFCBC0Gy5RdSyraLsZIaUYJiHaHm4E0OHmIoeDg5YEjPHG+BMz DMYP8YxTuH8ERFyYLCRFOKRGKxWtqrzNBaCBBPKQgxrDyKgvy+W10nlayJGx1xssksp72TvcX1cy foH9r3fZMh3O4JrMooZ9ww5RJGauoeOtug4oPKId6j63Z9wxjrUjhIm58qHQZuiWn9P8KNjCyfkt qD5yCQCD8lF+fKb94DOg2ieVUhlwknhdBBIIiwG+6fEc04v/zzEHWSSB6T4jrPhN/JvZQLRrAHn+ 8zuSGEFcWmKwYRMVIUh3cTXAQ1fJZrKQxsMG03VuT20gFNksUjMcTZWZSLnOJ++8wmwPJEj5KQXk 250wWCZc+so0IGSUDgZK0MPOHNyYO7MzuSqKo524mDZwxRSzWOLDGZra3k5pcX9RiK2YrnKQhiXl G+4S11FHA2is8pU4AojFlFHVCOYMEVhCkiHFIvUYU9wgx97B5r2gfC2hHD6m9hBxM5PK/dLSqvhI eJZCs7iy0CIRI3QNFQaGI1EDmOEddDC0iq9tRbmyGWseaoaxNOdpJmZzhfXpZBfpGsT/N9DA7TyU WkOCpSok+tZnDdFWz41KInDBLiLevj1dVQN0SasymYD0qqq9tk88JDB+f6+O7vorQh4uU8fIKFqS vFUq+dFh42SGzyldYP4zGAEtO39GgXhKCYugtCL1KKIpKhMJt2ttQKQYqxYIxVgI8xkAuecAw2bc OzhTOR3RY18cXTZmYy7ZDbnf6yt+yWq5aJ7VjUy4LRPJgCrvDgScTUqxzxKml6ghisGbNOqozvcj RjhGkzywYafIY3jDCmbDBRM2kUqUU1y1zMZgmbMi92T0zZqYaUtpIY0v+xrjibWZZSMM9SxpYnFH jkqlU1VPhIg2hdfYq2BRzoRz0c0K3UQKIxYkCKVLmtg5RvEbm0K0BrcFgewldG8J0IOprNYSQOeX U1CcTeFEiLVVGSMZvriGdDPKIHCDAHZGik60gHUhzbIrWETwUkMqEi+zg3MGjoYB01KikSvc2Npk omIcq9Ef8LD0UErphZvPBaLK0eJZrXxTbplFnZ3rS6jq7VheVFEgjCMoaRoopWAHfQFq30lQnYV3 ZgAgEUgMU3I8D+eyxQ5clzbeZi7bschV+ZhrEzQZ6znLnT1m/ZOrMkqGhr84hod60R2KI8g9zA3e 5OApClJHrLjAfAdnvDvOG/LybRIqp+MFRFlnt9JXaVZKtJpD08HVI2IswOkp/0dL1Sm+b4M9JPG5 hhZdCp7fMXQvH/N+O6+H4dtpGBnC6onOJUBlKSSqJR0lJynAaR/QtCR7l5RpUrnk2JZT+n0PONr2 JVEoohUR3vctahwKSO8sWFJ9JcsXI7pmtykj7aniUSeuoiKJUEpZzDvlLIV0lkBnUlyzKG/NfCDB 8rnbmLR+Z8Fmb5NUM0CEIMGEhIBID0nnNhYA5R53nHXc78m4sPbnzBjsOJmToBgsGCoIP1pD2RYG IMNpkOrQWjr1xSBCQSEdL0i7V0gpoxdz9nR2JM59r9TPxNWa53UkminqcPdKK4VRQsm/U6zxQ0Ca uurMjTJsCqD3ClCj1tGxTmg4iFgC2Zdh2lp84rvE2HObM3Bo257ZIm15lcVsfF4pJYFbPPl4JJc9 T+Z2vSn6vADd9wQKXXFwWKS6pfbSkrd6vF/8XckU4UJB1zrcBA== --===============6801600364784646883==--