From: Jonas Oreland Date: October 5 2011 1:42pm Subject: bzr push into mysql-5.1-telco-7.1 branch (jonas.oreland:4297 to 4298) List-Archive: http://lists.mysql.com/commits/141300 Message-Id: <20111005134248.DAC8D9410CE@perch.localdomain> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit 4298 Jonas Oreland 2011-10-05 [merge] ndb - merge 70 to 71 added: storage/ndb/tools/ndbinfo_select_all.cpp modified: storage/ndb/src/kernel/blocks/tsman.cpp storage/ndb/test/include/HugoQueryBuilder.hpp storage/ndb/test/run-test/atrt.hpp storage/ndb/test/run-test/daily-basic-tests.txt storage/ndb/test/run-test/daily-devel-tests.txt storage/ndb/test/run-test/files.cpp storage/ndb/test/run-test/main.cpp storage/ndb/test/run-test/setup.cpp storage/ndb/tools/CMakeLists.txt storage/ndb/tools/Makefile.am 4297 Jonas Oreland 2011-10-03 [merge] ndb - merge 70 to 71 modified: sql/ha_ndbcluster_binlog.cc storage/ndb/src/common/debugger/signaldata/ScanTab.cpp storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp storage/ndb/src/kernel/blocks/dbspj/Dbspj.hpp storage/ndb/src/kernel/blocks/dbspj/DbspjMain.cpp storage/ndb/src/ndbapi/NdbQueryBuilder.cpp storage/ndb/src/ndbapi/NdbQueryBuilderImpl.hpp storage/ndb/test/ndbapi/testNdbApi.cpp storage/ndb/test/ndbapi/testRestartGci.cpp storage/ndb/test/src/NDBT_Find.cpp === modified file 'storage/ndb/src/kernel/blocks/tsman.cpp' --- a/storage/ndb/src/kernel/blocks/tsman.cpp 2011-03-15 16:11:47 +0000 +++ b/storage/ndb/src/kernel/blocks/tsman.cpp 2011-10-04 07:56:40 +0000 @@ -488,7 +488,7 @@ Tsman::execDROP_FILEGROUP_IMPL_REQ(Signa if (ptr.p->m_ref_count) { jam(); - sendSignalWithDelay(reference(), GSN_DROP_FILEGROUP_REQ, signal, + sendSignalWithDelay(reference(), GSN_DROP_FILEGROUP_IMPL_REQ, signal, 100, signal->getLength()); return; } === modified file 'storage/ndb/test/include/HugoQueryBuilder.hpp' --- a/storage/ndb/test/include/HugoQueryBuilder.hpp 2011-07-04 16:30:34 +0000 +++ b/storage/ndb/test/include/HugoQueryBuilder.hpp 2011-10-05 13:41:47 +0000 @@ -60,15 +60,9 @@ public: /** * Query might table scan */ - O_TABLE_SCAN = 0x20, - - /** - * If not any options set, random query qill be created - */ - O_RANDOM_OPTIONS = (OptionMask)((~(OptionMask)0) & - ~(OptionMask)(O_SCAN | O_LOOKUP)) + O_TABLE_SCAN = 0x20 }; - static const OptionMask OM_RANDOM_OPTIONS = (OptionMask)O_RANDOM_OPTIONS; + static const OptionMask OM_RANDOM_OPTIONS = (OptionMask)(O_PK_INDEX | O_UNIQUE_INDEX | O_ORDERED_INDEX | O_TABLE_SCAN); HugoQueryBuilder(Ndb* ndb, const NdbDictionary::Table**tabptr, OptionMask om = OM_RANDOM_OPTIONS){ === modified file 'storage/ndb/test/run-test/atrt.hpp' --- a/storage/ndb/test/run-test/atrt.hpp 2011-06-30 16:04:23 +0000 +++ b/storage/ndb/test/run-test/atrt.hpp 2011-10-05 13:41:47 +0000 @@ -187,9 +187,23 @@ extern int g_baseport; extern int g_fqpn; extern int g_fix_nodeid; extern int g_default_ports; +extern int g_restart; extern const char * g_clusters; +/** + * Since binaries move location between 5.1 and 5.5 + * we keep full path to them here + */ +char * find_bin_path(const char * basename); +extern const char * g_ndb_mgmd_bin_path; +extern const char * g_ndbd_bin_path; +extern const char * g_ndbmtd_bin_path; +extern const char * g_mysqld_bin_path; +extern const char * g_mysql_install_db_bin_path; + +extern const char * g_search_path[]; + #ifdef _WIN32 #include === modified file 'storage/ndb/test/run-test/daily-basic-tests.txt' --- a/storage/ndb/test/run-test/daily-basic-tests.txt 2011-09-27 05:37:30 +0000 +++ b/storage/ndb/test/run-test/daily-basic-tests.txt 2011-10-05 13:18:31 +0000 @@ -96,6 +96,10 @@ max-time: 600 cmd: atrt-testBackup args: -n Bug57650 T1 +max-time: 1000 +cmd: atrt-testBackup +args: -n BackupBank T6 + # BASIC FUNCTIONALITY max-time: 500 cmd: testBasic @@ -356,6 +360,18 @@ max-time: 300 cmd: testIndex args: -n FireTrigOverload T1 +max-time: 500 +cmd: testIndex +args: -n Bug25059 -r 3000 T1 + +max-time: 2500 +cmd: testIndex +args: -l 2 -n SR1 T6 T13 + +max-time: 2500 +cmd: testIndex +args: -l 2 -n SR1_O T6 T13 + # # SCAN TESTS # @@ -713,6 +729,10 @@ args: -n NFNR1_O T6 T13 max-time: 2500 cmd: testIndex +args: -n NFNR2 T6 T13 + +max-time: 2500 +cmd: testIndex args: -n NFNR2_O T6 T13 max-time: 2500 @@ -809,6 +829,10 @@ max-time: 500 cmd: testDict args: -n Bug54651 T1 +max-time: 1500 +cmd: testDict +args: -n CreateMaxTables T6 + # # TEST NDBAPI # @@ -1034,6 +1058,14 @@ max-time: 5000 cmd: testSystemRestart args: -n SR_UNDO T6 +max-time: 1500 +cmd: testSystemRestart +args: -n SR3 T6 + +max-time: 1500 +cmd: testSystemRestart +args: -n SR4 T6 + # max-time: 5000 cmd: testSystemRestart @@ -1185,6 +1217,26 @@ max-time: 2500 cmd: testNodeRestart args: -n RestartRandomNodeError T6 T13 +max-time: 2500 +cmd: testNodeRestart +args: -l 1 -n MixedReadUpdateScan + +max-time: 2500 +cmd: testNodeRestart +args: -n Terror T6 T13 + +max-time: 3600 +cmd: testNodeRestart +args: -l 1 -n RestartNFDuringNR T6 T13 + +max-time: 3600 +cmd: testNodeRestart +args: -n RestartNodeDuringLCP T6 + +max-time: 2500 +cmd: testNodeRestart +args: -n FiftyPercentStopAndWait T6 T13 + # # MGMAPI AND MGSRV # @@ -1366,6 +1418,10 @@ max-time: 600 cmd: testSystemRestart args: -n Bug22696 T1 +max-time: 1000 +cmd: testSRBank +args: -n SR -l 300 -r 15 T1 + max-time: 600 cmd: testNodeRestart args: -n pnr --nologging T1 === modified file 'storage/ndb/test/run-test/daily-devel-tests.txt' --- a/storage/ndb/test/run-test/daily-devel-tests.txt 2011-06-30 15:59:25 +0000 +++ b/storage/ndb/test/run-test/daily-devel-tests.txt 2011-10-05 13:18:31 +0000 @@ -15,10 +15,6 @@ # # BACKUP # -max-time: 1000 -cmd: atrt-testBackup -args: -n BackupBank T6 - max-time: 500 cmd: testNdbApi args: -n MaxOperations T1 T6 T13 @@ -27,18 +23,6 @@ max-time: 1500 cmd: testDict args: -n CreateTableWhenDbIsFull T6 -max-time: 1500 -cmd: testDict -args: -n CreateMaxTables T6 - -max-time: 500 -cmd: testIndex -args: -n Bug25059 -r 3000 T1 - -max-time: 1000 -cmd: testSRBank -args: -n SR -l 300 -r 15 T1 - max-time: 1000 cmd: testSRBank args: -n NR -l 300 -r 15 T1 @@ -51,14 +35,6 @@ args: -n Mix -l 300 -r 15 T1 # # SYSTEM RESTARTS # -max-time: 1500 -cmd: testSystemRestart -args: -n SR3 T6 - -max-time: 1500 -cmd: testSystemRestart -args: -n SR4 T6 - # max-time: 1500 cmd: testSystemRestart @@ -73,28 +49,8 @@ args: -l 1 -n MixedPkReadPkUpdate max-time: 2500 cmd: testNodeRestart -args: -l 1 -n MixedReadUpdateScan - -max-time: 2500 -cmd: testNodeRestart -args: -n Terror T6 T13 - -max-time: 2500 -cmd: testNodeRestart args: -n FullDb T6 T13 -max-time: 3600 -cmd: testNodeRestart -args: -l 1 -n RestartNFDuringNR T6 T13 - -max-time: 3600 -cmd: testNodeRestart -args: -n RestartNodeDuringLCP T6 - -max-time: 2500 -cmd: testNodeRestart -args: -n FiftyPercentStopAndWait T6 T13 - max-time: 500 cmd: testNodeRestart args: -n Bug16772 T1 @@ -106,24 +62,12 @@ args: -n Bug16772 T1 # max-time: 2500 cmd: testIndex -args: -n NFNR2 T6 T13 - -max-time: 2500 -cmd: testIndex args: -n NFNR3 T6 T13 max-time: 2500 cmd: testIndex -args: -l 2 -n SR1 T6 T13 - -max-time: 2500 -cmd: testIndex args: -n NFNR3_O T6 T13 -max-time: 2500 -cmd: testIndex -args: -l 2 -n SR1_O T6 T13 - # dict trans max-time: 1800 cmd: testDict === modified file 'storage/ndb/test/run-test/files.cpp' --- a/storage/ndb/test/run-test/files.cpp 2011-06-30 15:59:25 +0000 +++ b/storage/ndb/test/run-test/files.cpp 2011-10-03 08:46:52 +0000 @@ -179,8 +179,8 @@ setup_files(atrt_config& config, int set const char * val; require(proc.m_options.m_loaded.get("--datadir=", &val)); BaseString tmp; - tmp.assfmt("%s/bin/mysql_install_db --defaults-file=%s/my.cnf --datadir=%s > %s/mysql_install_db.log 2>&1", - g_prefix, g_basedir, val, proc.m_proc.m_cwd.c_str()); + tmp.assfmt("%s --defaults-file=%s/my.cnf --datadir=%s > %s/mysql_install_db.log 2>&1", + g_mysql_install_db_bin_path, g_basedir, val, proc.m_proc.m_cwd.c_str()); to_fwd_slashes(tmp); if (sh(tmp.c_str()) != 0) @@ -305,8 +305,13 @@ setup_files(atrt_config& config, int set } fprintf(fenv, "\"\nexport CMD\n"); } - - fprintf(fenv, "PATH=%s/bin:%s/libexec:$PATH\n", g_prefix, g_prefix); + + fprintf(fenv, "PATH="); + for (int i = 0; g_search_path[i] != 0; i++) + { + fprintf(fenv, "%s/%s:", g_prefix, g_search_path[i]); + } + fprintf(fenv, "$PATH\n"); keys.push_back("PATH"); for (size_t k = 0; km_hostname.length() == 0) + continue; + if(config.m_hosts[i]->m_cpcd->connect() != 0){ g_logger.error("Unable to connect to cpc %s:%d", config.m_hosts[i]->m_cpcd->getHost(), @@ -1088,7 +1133,11 @@ update_status(atrt_config& config, int){ Vector dummy; m_procs.fill(config.m_hosts.size(), dummy); - for(size_t i = 0; im_hostname.length() == 0) + continue; + Properties p; config.m_hosts[i]->m_cpcd->list_processes(m_procs[i], p); } @@ -1261,11 +1310,14 @@ setup_test_case(atrt_config& config, con proc.m_type == atrt_process::AP_CLIENT) { BaseString cmd; - if (tc.m_command.c_str()[0] != '/') + char * p = find_bin_path(tc.m_command.c_str()); + if (p == 0) { - cmd.appfmt("%s/bin/", g_prefix); + g_logger.critical("Failed to locate '%s'", tc.m_command.c_str()); + return false; } - cmd.append(tc.m_command.c_str()); + cmd.assign(p); + free(p); if (0) // valgrind { @@ -1299,6 +1351,9 @@ gather_result(atrt_config& config, int * for(size_t i = 0; im_hostname.length() == 0) + continue; + tmp.appfmt(" %s:%s/*", config.m_hosts[i]->m_hostname.c_str(), config.m_hosts[i]->m_basedir.c_str()); @@ -1333,7 +1388,10 @@ setup_hosts(atrt_config& config){ return false; } - for(size_t i = 0; im_hostname.length() == 0) + continue; BaseString tmp = g_setup_progname; tmp.appfmt(" %s %s/ %s/", config.m_hosts[i]->m_hostname.c_str(), @@ -1375,6 +1433,9 @@ deploy(int d, atrt_config & config) { for (size_t i = 0; im_hostname.length() == 0) + continue; + if (d & 1) { if (!do_rsync(g_basedir, config.m_hosts[i]->m_hostname.c_str())) @@ -1509,6 +1570,35 @@ reset_config(atrt_config & config) return changed; } +static +bool +find_binaries() +{ + g_logger.info("Locating binaries..."); + bool ok = true; + for (int i = 0; g_binaries[i].exe != 0; i++) + { + const char * p = find_bin_path(g_binaries[i].exe); + if (p == 0) + { + if (g_binaries[i].is_required) + { + g_logger.critical("Failed to locate '%s'", g_binaries[i].exe); + ok = false; + } + else + { + g_logger.info("Failed to locate '%s'...ok", g_binaries[i].exe); + } + } + else + { + * g_binaries[i].var = p; + } + } + return ok; +} + template class Vector >; template class Vector; template class Vector; === modified file 'storage/ndb/test/run-test/setup.cpp' --- a/storage/ndb/test/run-test/setup.cpp 2011-06-30 15:59:25 +0000 +++ b/storage/ndb/test/run-test/setup.cpp 2011-10-03 11:06:06 +0000 @@ -20,6 +20,7 @@ #include #include #include +#include extern int g_mt; extern int g_mt_rr; @@ -321,7 +322,7 @@ load_process(atrt_config& config, atrt_c case atrt_process::AP_NDB_MGMD: { proc.m_proc.m_name.assfmt("%u-%s", proc_no, "ndb_mgmd"); - proc.m_proc.m_path.assign(g_prefix).append("/libexec/ndb_mgmd"); + proc.m_proc.m_path.assign(g_ndb_mgmd_bin_path); proc.m_proc.m_args.assfmt("--defaults-file=%s/my.cnf", proc.m_host->m_basedir.c_str()); proc.m_proc.m_args.appfmt(" --defaults-group-suffix=%s", @@ -336,13 +337,15 @@ load_process(atrt_config& config, atrt_c } case atrt_process::AP_NDBD: { - if (g_mt == 0 || (g_mt == 1 && ((g_mt_rr++) & 1) == 0)) + if (g_mt == 0 || + (g_mt == 1 && ((g_mt_rr++) & 1) == 0) || + g_ndbmtd_bin_path == 0) { - proc.m_proc.m_path.assign(g_prefix).append("/libexec/ndbd"); + proc.m_proc.m_path.assign(g_ndbd_bin_path); } else { - proc.m_proc.m_path.assign(g_prefix).append("/libexec/ndbmtd"); + proc.m_proc.m_path.assign(g_ndbmtd_bin_path); } proc.m_proc.m_name.assfmt("%u-%s", proc_no, "ndbd"); @@ -350,7 +353,9 @@ load_process(atrt_config& config, atrt_c proc.m_host->m_basedir.c_str()); proc.m_proc.m_args.appfmt(" --defaults-group-suffix=%s", cluster.m_name.c_str()); - proc.m_proc.m_args.append(" --nodaemon --initial -n"); + proc.m_proc.m_args.append(" --nodaemon -n"); + if (!g_restart) + proc.m_proc.m_args.append(" --initial"); if (g_fix_nodeid) proc.m_proc.m_args.appfmt(" --ndb-nodeid=%u", proc.m_nodeid); proc.m_proc.m_cwd.assfmt("%sndbd.%u", dir.c_str(), proc.m_index); @@ -361,7 +366,7 @@ load_process(atrt_config& config, atrt_c case atrt_process::AP_MYSQLD: { proc.m_proc.m_name.assfmt("%u-%s", proc_no, "mysqld"); - proc.m_proc.m_path.assign(g_prefix).append("/libexec/mysqld"); + proc.m_proc.m_path.assign(g_mysqld_bin_path); proc.m_proc.m_args.assfmt("--defaults-file=%s/my.cnf", proc.m_host->m_basedir.c_str()); proc.m_proc.m_args.appfmt(" --defaults-group-suffix=.%d%s", @@ -1037,3 +1042,28 @@ operator<<(NdbOut& out, const atrt_proce return out; } +char * +find_bin_path(const char * exe) +{ + if (exe == 0) + return 0; + + if (exe[0] == '/') + { + /** + * Trust that path is correct... + */ + return strdup(exe); + } + + for (int i = 0; g_search_path[i] != 0; i++) + { + BaseString p; + p.assfmt("%s/%s/%s", g_prefix, g_search_path[i], exe); + if (File_class::exists(p.c_str())) + { + return strdup(p.c_str()); + } + } + return 0; +} === modified file 'storage/ndb/tools/CMakeLists.txt' --- a/storage/ndb/tools/CMakeLists.txt 2011-07-04 16:30:34 +0000 +++ b/storage/ndb/tools/CMakeLists.txt 2011-10-05 13:41:47 +0000 @@ -91,6 +91,11 @@ ADD_EXECUTABLE(ndb_dump_frm_data ndb_dump_frm_data.cpp) TARGET_LINK_LIBRARIES(ndb_dump_frm_data ndbNDBT ndbgeneral) +MYSQL_ADD_EXECUTABLE(ndbinfo_select_all + ndbinfo_select_all.cpp + COMPONENT ClusterTools) +TARGET_LINK_LIBRARIES(ndbinfo_select_all ndbNDBT) + IF (MYSQL_VERSION_ID LESS "50501") # Don't build or install this program anymore in 5.5+ ADD_EXECUTABLE(ndb_test_platform ndb_test_platform.cpp) === modified file 'storage/ndb/tools/Makefile.am' --- a/storage/ndb/tools/Makefile.am 2011-09-04 17:04:25 +0000 +++ b/storage/ndb/tools/Makefile.am 2011-10-05 13:41:47 +0000 @@ -32,7 +32,8 @@ ndbtools_PROGRAMS = \ ndb_select_all \ ndb_select_count \ ndb_restore ndb_config \ - ndb_index_stat + ndb_index_stat \ + ndbinfo_select_all tools_common_sources = ../test/src/NDBT_ReturnCodes.cpp \ ../test/src/NDBT_Table.cpp \ @@ -81,6 +82,7 @@ ndbinfo.sql: $(ndbinfo_sql_SOURCES) ndb_index_stat_SOURCES = ndb_index_stat.cpp $(tools_common_sources) ndb_dump_frm_data_SOURCES = ndb_dump_frm_data.cpp +ndbinfo_select_all_SOURCES = ndbinfo_select_all.cpp include $(top_srcdir)/storage/ndb/config/common.mk.am include $(top_srcdir)/storage/ndb/config/type_ndbapitools.mk.am @@ -99,4 +101,5 @@ ndb_config_LDFLAGS = @ndb_bin_am_ldflags ndbinfo_sql_LDFLAGS = @ndb_bin_am_ldflags@ ndb_index_stat_LDFLAGS = @ndb_bin_am_ldflags@ ndb_dump_frm_data_LDFLAGS = @ndb_bin_am_ldflags@ +ndbinfo_select_all_LDFLAGS = @ndb_bin_am_ldflags@ === added file 'storage/ndb/tools/ndbinfo_select_all.cpp' --- a/storage/ndb/tools/ndbinfo_select_all.cpp 1970-01-01 00:00:00 +0000 +++ b/storage/ndb/tools/ndbinfo_select_all.cpp 2011-10-05 11:21:23 +0000 @@ -0,0 +1,175 @@ +/* + Copyright (c) 2003, 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 +#include + +#include +#include +#include +#include "../src/ndbapi/NdbInfo.hpp" + +const char *load_default_groups[]= { "mysql_cluster",0 }; + +static struct my_option my_long_options[] = +{ + NDB_STD_OPTS("ndbinfo_select_all"), + { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} +}; + + +static void short_usage_sub(void) +{ + ndb_short_usage_sub(NULL); +} + +static void usage() +{ + ndb_usage(short_usage_sub, load_default_groups, my_long_options); +} + +int +main(int argc, char** argv) +{ + NDB_INIT(argv[0]); + ndb_opt_set_usage_funcs(short_usage_sub, usage); + load_defaults("my",load_default_groups,&argc,&argv); + int ho_error; +#ifndef DBUG_OFF + opt_debug= "d:t:O,/tmp/ndbinfo_select_all.trace"; +#endif + if ((ho_error=handle_options(&argc, &argv, my_long_options, + ndb_std_get_one_option))) + return 1; + + if (argv[0] == 0) + { + return 0; + } + + Ndb_cluster_connection con(opt_ndb_connectstring, opt_ndb_nodeid); + con.set_name("ndbinfo_select_all"); + if(con.connect(12, 5, 1) != 0) + { + ndbout << "Unable to connect to management server." << endl; + return 1; + } + + if (con.wait_until_ready(30,0) < 0) + { + ndbout << "Cluster nodes not ready in 30 seconds." << endl; + return 1; + } + + NdbInfo info(&con, ""); + if (!info.init()) + { + ndbout << "Failed to init ndbinfo!" << endl; + return 1; + } + + const Uint32 batchsizerows = 32; + + for (int ii = 0; argv[ii] != 0; ii++) + { + ndbout << "== " << argv[ii] << " ==" << endl; + + const NdbInfo::Table * pTab = 0; + int res = info.openTable(argv[ii], &pTab); + if (res != 0) + { + ndbout << "Failed to open: " << argv[ii] << ", res: " << res << endl; + continue; + } + + unsigned cols = pTab->columns(); + for (unsigned i = 0; igetColumn(i); + ndbout << pCol->m_name.c_str() << "\t"; + } + ndbout << endl; + + NdbInfoScanOperation * pScan = 0; + res= info.createScanOperation(pTab, &pScan, batchsizerows); + if (res != 0) + { + ndbout << "Failed to createScan: " << argv[ii] << ", res: " << res<< endl; + info.closeTable(pTab); + continue; + } + + if (pScan->readTuples() != 0) + { + ndbout << "scanOp->readTuples failed" << endl; + return 1; + } + + Vector recAttrs; + for (unsigned i = 0; igetValue(i); + if (pRec == 0) + { + ndbout << "Failed to getValue(" << i << ")" << endl; + return 1; + } + recAttrs.push_back(pRec); + } + + if(pScan->execute() != 0) + { + ndbout << "scanOp->execute failed" << endl; + return 1; + } + + while(pScan->nextResult() == 1) + { + for (unsigned i = 0; iisNULL()) + { + ndbout << "NULL"; + } + else + { + switch(pTab->getColumn(i)->m_type){ + case NdbInfo::Column::String: + ndbout << recAttrs[i]->c_str(); + break; + case NdbInfo::Column::Number: + ndbout << recAttrs[i]->u_32_value(); + break; + case NdbInfo::Column::Number64: + ndbout << recAttrs[i]->u_64_value(); + break; + } + } + ndbout << "\t"; + } + ndbout << endl; + } + + info.releaseScanOperation(pScan); + info.closeTable(pTab); + } + + return 0; +} + +template class Vector; No bundle (reason: useless for push emails).