diff options
Diffstat (limited to 'mysql-test/mysql-test-run.pl')
-rwxr-xr-x | mysql-test/mysql-test-run.pl | 328 |
1 files changed, 238 insertions, 90 deletions
diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 526077a7de1..a06b29815a0 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -126,13 +126,9 @@ my $path_config_file; # The generated config file, var/my.cnf # executables will be used by the test suite. our $opt_vs_config = $ENV{'MTR_VS_CONFIG'}; -my $DEFAULT_SUITES= "binlog,federated,main,maria,rpl,innodb,parts"; +my $DEFAULT_SUITES= "main,binlog,federated,rpl,maria,parts"; +my $opt_suites; -our $opt_usage; -our $opt_list_options; -our $opt_suites; -our $opt_suites_default= "main,backup,backup_engines,binlog,rpl,parts"; # Default suites to run -our $opt_script_debug= 0; # Script debugging, enable with --script-debug our $opt_verbose= 0; # Verbose output, enable with --verbose our $exe_mysql; our $exe_mysqladmin; @@ -150,7 +146,7 @@ our @opt_extra_mysqltest_opt; my $opt_compress; my $opt_ssl; my $opt_skip_ssl; -my $opt_ssl_supported; +our $opt_ssl_supported; my $opt_ps_protocol; my $opt_sp_protocol; my $opt_cursor_protocol; @@ -205,10 +201,10 @@ my $opt_mark_progress; my $opt_sleep; -my $opt_testcase_timeout= 15; # 15 minutes -my $opt_suite_timeout = 360; # 6 hours -my $opt_shutdown_timeout= 10; # 10 seconds -my $opt_start_timeout = 180; # 180 seconds +my $opt_testcase_timeout= $ENV{MTR_TESTCASE_TIMEOUT} || 15; # minutes +my $opt_suite_timeout = $ENV{MTR_SUITE_TIMEOUT} || 360; # minutes +my $opt_shutdown_timeout= $ENV{MTR_SHUTDOWN_TIMEOUT} || 10; # seconds +my $opt_start_timeout = $ENV{MTR_START_TIMEOUT} || 180; # seconds sub testcase_timeout { return $opt_testcase_timeout * 60; }; sub suite_timeout { return $opt_suite_timeout * 60; }; @@ -216,9 +212,10 @@ sub check_timeout { return $opt_testcase_timeout * 6; }; my $opt_start; my $opt_start_dirty; +my $start_only; my $opt_wait_all; my $opt_repeat= 1; -my $opt_retry= 3; +my $opt_retry= 1; my $opt_retry_failure= 2; my $opt_strace_client; @@ -292,11 +289,6 @@ sub main { "mysql-5.1-telco-6.2-merge" => "ndb_team", "mysql-5.1-telco-6.3" => "ndb_team", "mysql-6.0-ndb" => "ndb_team", - "mysql-6.0-falcon" => "falcon_team", - "mysql-6.0-falcon-team" => "falcon_team", - "mysql-6.0-falcon-wlad" => "falcon_team", - "mysql-6.0-falcon-chris" => "falcon_team", - "mysql-6.0-falcon-kevin" => "falcon_team", ); foreach my $dir ( reverse splitdir($basedir) ) { @@ -309,6 +301,9 @@ sub main { } } + # Check for plugin availability so we know whether to skip tests or not. + detect_plugins(); + mtr_report("Collecting tests..."); my $tests= collect_test_cases($opt_suites, \@opt_cases); @@ -339,7 +334,8 @@ sub main { for my $limit (2000, 1500, 1000, 500){ $opt_parallel-- if ($sys_info->min_bogomips() < $limit); } - $opt_parallel= 8 if ($opt_parallel > 8); + my $max_par= $ENV{MTR_MAX_PARALLEL} || 8; + $opt_parallel= $max_par if ($opt_parallel > $max_par); $opt_parallel= $num_tests if ($opt_parallel > $num_tests); $opt_parallel= 1 if (IS_WINDOWS and $sys_info->isvm()); $opt_parallel= 1 if ($opt_parallel < 1); @@ -417,7 +413,6 @@ sub main { # Not all tests completed, failure mtr_report(); mtr_report("Only ", int(@$completed), " of $num_tests completed."); - mtr_error("Not all tests completed"); } mtr_print_line(); @@ -429,6 +424,10 @@ sub main { mtr_report_stats($fail, $completed, $extra_warnings); + if ( @$completed != $num_tests) + { + mtr_error("Not all tests completed"); + } exit(0); } @@ -547,7 +546,8 @@ sub run_test_server ($$$) { } } $num_saved_datadir++; - $num_failed_test++ unless $result->{retries}; + $num_failed_test++ unless ($result->{retries} || + $result->{exp_fail}); $test_failure= 1; if ( !$opt_force ) { @@ -618,8 +618,9 @@ sub run_test_server ($$$) { my $fake_test= My::Test::read_test($sock); my $test_list= join (" ", @{$fake_test->{testnames}}); push @$extra_warnings, $test_list; + my $report= $fake_test->{'warnings'}; mtr_report("***Warnings generated in error logs during shutdown ". - "after running tests: $test_list"); + "after running tests: $test_list\n\n$report"); $test_failure= 1; if ( !$opt_force ) { # Test failure due to warnings, force is off @@ -1052,6 +1053,9 @@ sub command_line_setup { if ( $opt_experimental ) { + # $^O on Windows considered not generic enough + my $plat= (IS_WINDOWS) ? 'windows' : $^O; + # read the list of experimental test cases from the file specified on # the command line open(FILE, "<", $opt_experimental) or mtr_error("Can't read experimental file: $opt_experimental"); @@ -1062,6 +1066,15 @@ sub command_line_setup { # remove comments (# foo) at the beginning of the line, or after a # blank at the end of the line s/( +|^)#.*$//; + # If @ platform specifier given, use this entry only if it contains + # @<platform> or @!<xxx> where xxx != platform + if (/\@.*/) + { + next if (/\@!$plat/); + next unless (/\@$plat/ or /\@!/); + # Then remove @ and everything after it + s/\@.*$//; + } # remove whitespace s/^ +//; s/ +$//; @@ -1321,6 +1334,21 @@ sub command_line_setup { { mtr_error("Can't use --extern when using debugger"); } + # Set one week timeout (check-testcase timeout will be 1/10th) + $opt_testcase_timeout= 7 * 24 * 60; + $opt_suite_timeout= 7 * 24 * 60; + # One day to shutdown + $opt_shutdown_timeout= 24 * 60; + # One day for PID file creation (this is given in seconds not minutes) + $opt_start_timeout= 24 * 60 * 60; + } + + # -------------------------------------------------------------------------- + # Modified behavior with --start options + # -------------------------------------------------------------------------- + if ($opt_start or $opt_start_dirty) { + collect_option ('quick-collect', 1); + $start_only= 1; } if ($opt_debug) { @@ -1334,7 +1362,7 @@ sub command_line_setup { # Check use of wait-all # -------------------------------------------------------------------------- - if ($opt_wait_all && ! ($opt_start_dirty || $opt_start)) + if ($opt_wait_all && ! $start_only) { mtr_error("--wait-all can only be used with --start or --start-dirty"); } @@ -1394,6 +1422,9 @@ sub command_line_setup { push(@valgrind_args, @default_valgrind_args) unless @valgrind_args; + # Make valgrind run in quiet mode so it only print errors + push(@valgrind_args, "--quiet" ); + mtr_report("Running valgrind with options \"", join(" ", @valgrind_args), "\""); } @@ -1609,6 +1640,10 @@ sub collect_mysqld_features_from_running_server () } } + # "Convert" innodb flag + $mysqld_variables{'innodb'}= "ON" + if ($mysqld_variables{'have_innodb'} eq "YES"); + # Parse version my $version_str= $mysqld_variables{'version'}; if ( $version_str =~ /^([0-9]*)\.([0-9]*)\.([0-9]*)/ ) @@ -1848,6 +1883,37 @@ sub have_maria_support () { } +# Detect plugin presense and set environment variables appropriately. +# This needs to be done early, so we can know whether to skip tests. +sub detect_plugins { + # -------------------------------------------------------------------------- + # Add the path where mysqld will find ha_example.so + # -------------------------------------------------------------------------- + if ($mysql_version_id >= 50100) { + my $plugin_filename; + if (IS_WINDOWS) + { + $plugin_filename = "ha_example.dll"; + } + else + { + $plugin_filename = "ha_example.so"; + } + my $lib_example_plugin= + mtr_file_exists(vs_config_dirs('storage/example',$plugin_filename), + "$basedir/storage/example/.libs/".$plugin_filename, + "$basedir/lib/mariadb/plugin/".$plugin_filename, + "$basedir/lib/mysql/plugin/".$plugin_filename); + $ENV{'EXAMPLE_PLUGIN'}= + ($lib_example_plugin ? basename($lib_example_plugin) : ""); + $ENV{'EXAMPLE_PLUGIN_OPT'}= "--plugin-dir=". + ($lib_example_plugin ? dirname($lib_example_plugin) : ""); + + $ENV{'HA_EXAMPLE_SO'}="'".$plugin_filename."'"; + $ENV{'EXAMPLE_PLUGIN_LOAD'}="--plugin_load=EXAMPLE=".$plugin_filename; + } +} + # # Set environment to be used by childs of this process for # things that are constant during the whole lifetime of mysql-test-run @@ -1906,33 +1972,6 @@ sub environment_setup { $ENV{'UDF_EXAMPLE_LIB_OPT'}= "--plugin-dir=". ($lib_udf_example ? dirname($lib_udf_example) : ""); - # -------------------------------------------------------------------------- - # Add the path where mysqld will find ha_example.so - # -------------------------------------------------------------------------- - if ($mysql_version_id >= 50100 && !(IS_WINDOWS && $opt_embedded_server)) { - my $plugin_filename; - if (IS_WINDOWS) - { - $plugin_filename = "ha_example.dll"; - } - else - { - $plugin_filename = "ha_example.so"; - } - my $lib_example_plugin= - mtr_file_exists(vs_config_dirs('storage/example',$plugin_filename), - "$basedir/storage/example/.libs/".$plugin_filename, - "$basedir/lib/mariadb/plugin/".$plugin_filename, - "$basedir/lib/mysql/plugin/".$plugin_filename); - $ENV{'EXAMPLE_PLUGIN'}= - ($lib_example_plugin ? basename($lib_example_plugin) : ""); - $ENV{'EXAMPLE_PLUGIN_OPT'}= "--plugin-dir=". - ($lib_example_plugin ? dirname($lib_example_plugin) : ""); - - $ENV{'HA_EXAMPLE_SO'}="'".$plugin_filename."'"; - $ENV{'EXAMPLE_PLUGIN_LOAD'}="--plugin_load=;EXAMPLE=".$plugin_filename.";"; - } - # ---------------------------------------------------- # Add the path where mysqld will find mypluglib.so # ---------------------------------------------------- @@ -3004,7 +3043,7 @@ sub run_testcase_check_skip_test($) if ( $tinfo->{'skip'} ) { - mtr_report_test_skipped($tinfo); + mtr_report_test_skipped($tinfo) unless $start_only; return 1; } @@ -3174,7 +3213,8 @@ test case was executed:\n"; # Unknown process returned, most likley a crash, abort everything $tinfo->{comment}= "The server $proc crashed while running ". - "'check testcase $mode test'"; + "'check testcase $mode test'". + get_log_from_proc($proc, $tinfo->{name}); $result= 3; } @@ -3292,7 +3332,8 @@ sub run_on_all($$) else { # Unknown process returned, most likley a crash, abort everything $tinfo->{comment}.= - "The server $proc crashed while running '$run'"; + "The server $proc crashed while running '$run'". + get_log_from_proc($proc, $tinfo->{name}); } # Kill any check processes still running @@ -3387,7 +3428,7 @@ sub restart_forced_by_test # Return timezone value of tinfo or default value sub timezone { my ($tinfo)= @_; - return $tinfo->{timezone} || "GMT-3"; + return $tinfo->{timezone} || "DEFAULT"; } @@ -3407,11 +3448,21 @@ sub run_testcase ($$) { mtr_verbose("Running test:", $tinfo->{name}); + # Allow only alpanumerics pluss _ - + . in combination names + my $combination= $tinfo->{combination}; + if ($combination && $combination !~ /^\w[-\w\.\+]+$/) + { + mtr_error("Combination '$combination' contains illegal characters"); + } # ------------------------------------------------------- # Init variables that can change between each test case # ------------------------------------------------------- my $timezone= timezone($tinfo); - $ENV{'TZ'}= $timezone; + if ($timezone ne 'DEFAULT') { + $ENV{'TZ'}= $timezone; + } else { + delete($ENV{'TZ'}); + } mtr_verbose("Setting timezone: $timezone"); if ( ! using_extern() ) @@ -3501,9 +3552,16 @@ sub run_testcase ($$) { # server exits # ---------------------------------------------------------------------- - if ( $opt_start or $opt_start_dirty ) + if ( $start_only ) { mtr_print("\nStarted", started(all_servers())); + mtr_print("Using config for test", $tinfo->{name}); + mtr_print("Port and socket path for server(s):"); + foreach my $mysqld ( mysqlds() ) + { + mtr_print ($mysqld->name() . " " . $mysqld->value('port') . + " " . $mysqld->value('socket')); + } mtr_print("Waiting for server(s) to exit..."); if ( $opt_wait_all ) { My::SafeProcess->wait_all(); @@ -3597,7 +3655,7 @@ sub run_testcase ($$) { my $check_res; if ( restart_forced_by_test() ) { - stop_all_servers(); + stop_all_servers($opt_shutdown_timeout); } elsif ( $opt_check_testcases and $check_res= check_testcase($tinfo, "after")) @@ -3614,7 +3672,7 @@ sub run_testcase ($$) { # test. } else { # Not checking warnings, so can do a hard shutdown. - stop_all_servers(); + stop_all_servers($opt_shutdown_timeout); } mtr_report("Resuming tests...\n"); } @@ -3696,7 +3754,8 @@ sub run_testcase ($$) { { # Server failed, probably crashed $tinfo->{comment}= - "Server $proc failed during test run"; + "Server $proc failed during test run" . + get_log_from_proc($proc, $tinfo->{name}); # ---------------------------------------------------- # It's not mysqltest that has exited, kill it @@ -3809,6 +3868,64 @@ sub pre_write_errorlog { } } +# Extract server log from after the last occurrence of named test +# Return as an array of lines +# + +sub extract_server_log ($$) { + my ($error_log, $tname) = @_; + + # Open the servers .err log file and read all lines + # belonging to current test into @lines + my $Ferr = IO::File->new($error_log) + or mtr_error("Could not open file '$error_log' for reading: $!"); + + my @lines; + my $found_test= 0; # Set once we've found the log of this test + while ( my $line = <$Ferr> ) + { + if ($found_test) + { + # If test wasn't last after all, discard what we found, test again. + if ( $line =~ /^CURRENT_TEST:/) + { + @lines= (); + $found_test= $line =~ /^CURRENT_TEST: $tname/; + } + else + { + push(@lines, $line); + } + } + else + { + # Search for beginning of test, until found + $found_test= 1 if ($line =~ /^CURRENT_TEST: $tname/); + } + } + $Ferr = undef; # Close error log file + + return @lines; +} + +# Get log from server identified from its $proc object, from named test +# Return as a single string +# + +sub get_log_from_proc ($$) { + my ($proc, $name)= @_; + my $srv_log= ""; + + foreach my $mysqld (mysqlds()) { + if ($mysqld->{proc} eq $proc) { + my @srv_lines= extract_server_log($mysqld->value('#log-error'), $name); + $srv_log= "\nServer log from this test:\n" . join ("", @srv_lines); + last; + } + } + return $srv_log; +} + # # Perform a rough examination of the servers # error log and write all lines that look @@ -3858,16 +3975,9 @@ sub extract_warning_lines ($) { my @patterns = ( - # The patterns for detection of [Warning] and [ERROR] - # in the server log files have been faulty for a longer period - # and correcting them shows a few additional harmless warnings. - # Thus those patterns are temporarily removed from the list - # of patterns. For more info see BUG#42408 - # qr/^Warning:|mysqld: Warning|\[Warning\]/, - # qr/^Error:|\[ERROR\]/, - qr/^Warning:|mysqld: Warning/, - qr/^Error:/, - qr/^==.* at 0x/, + qr/^Warning:|mysqld: Warning|\[Warning\]/, + qr/^Error:|\[ERROR\]/, + qr/^==\d*==/, # valgrind errors qr/InnoDB: Warning|InnoDB: Error/, qr/^safe_mutex:|allocated at line/, qr/missing DBUG_RETURN/, @@ -3886,11 +3996,33 @@ sub extract_warning_lines ($) { # Perl code. my @antipatterns = ( + qr/error .*connecting to master/, qr/InnoDB: Error: in ALTER TABLE `test`.`t[12]`/, qr/InnoDB: Error: table `test`.`t[12]` does not exist in the InnoDB internal/, + qr/Slave: Unknown table 't1' Error_code: 1051/, + qr/Slave SQL:.*(Error_code: [[:digit:]]+|Query:.*)/, + qr/slave SQL thread aborted/, + qr/unknown option '--loose-/, + qr/unknown variable 'loose-/, + qr/Now setting lower_case_table_names to [02]/, + qr/Setting lower_case_table_names=2/, + qr/deprecated/, + qr/Slave SQL thread retried transaction/, + qr/Slave \(additional info\)/, + qr/Incorrect information in file/, + qr/Slave I\/O: Get master SERVER_ID failed with error:.*/, + qr/Slave I\/O: Get master clock failed with error:.*/, + qr/Slave I\/O: Get master COLLATION_SERVER failed with error:.*/, + qr/Slave I\/O: Get master TIME_ZONE failed with error:.*/, + qr/Slave I\/O: error reconnecting to master '.*' - retry-time: [1-3] retries/, + qr/Error reading packet/, + qr/Slave: Can't drop database.* database doesn't exist/, + qr/Slave: Operation DROP USER failed for 'create_rout_db'/, + qr|Checking table: '\./mtr/test_suppressions'|, + qr|mysqld: Table '\./mtr/test_suppressions' is marked as crashed and should be repaired| ); - my $match_count= 0; + my $matched_lines= []; LINE: foreach my $line ( @lines ) { PAT: foreach my $pat ( @patterns ) @@ -3902,18 +4034,18 @@ sub extract_warning_lines ($) { next LINE if $line =~ $apat; } print $Fwarn $line; - ++$match_count; + push @$matched_lines, $line; last PAT; } } } $Fwarn = undef; # Close file - if ($match_count > 0 && + if (scalar(@$matched_lines) > 0 && defined($last_warning_position->{$error_log}{test_names})) { - return $last_warning_position->{$error_log}{test_names}; + return ($last_warning_position->{$error_log}{test_names}, $matched_lines); } else { - return []; + return ([], $matched_lines); } } @@ -3970,7 +4102,7 @@ sub start_check_warnings ($$) { error => $errfile, output => $errfile, args => \$args, - user_data => $errfile, + user_data => [$errfile, $mysqld], verbose => $opt_verbose, ); mtr_verbose("Started $proc"); @@ -3980,7 +4112,7 @@ sub start_check_warnings ($$) { # # Loop through our list of processes and check the error log -# for unexepcted errors and warnings +# for unexpected errors and warnings # sub check_warnings ($) { my ($tinfo)= @_; @@ -4016,7 +4148,7 @@ sub check_warnings ($) { if ( delete $started{$proc->pid()} ) { # One check warning process returned my $res= $proc->exit_status(); - my $err_file= $proc->user_data(); + my ($err_file, $mysqld)= @{$proc->user_data()}; if ( $res == 0 or $res == 62 ){ @@ -4052,7 +4184,8 @@ sub check_warnings ($) { my $report= mtr_grab_file($err_file); $tinfo->{comment}.= "Could not execute 'check-warnings' for ". - "testcase '$tname' (res: $res):\n"; + "testcase '$tname' (res: $res) server: '". + $mysqld->name() .":\n"; $tinfo->{comment}.= $report; $result= 2; @@ -4067,7 +4200,8 @@ sub check_warnings ($) { else { # Unknown process returned, most likley a crash, abort everything $tinfo->{comment}= - "The server $proc crashed while running 'check warnings'"; + "The server $proc crashed while running 'check warnings'". + get_log_from_proc($proc, $tinfo->{name}); $result= 3; } @@ -4083,18 +4217,24 @@ sub check_warnings ($) { } # Check for warnings generated during shutdown of a mysqld server. -# If any, report them to master server, and return true; else just return false. +# If any, report them to master server, and return true; else just return +# false. + sub check_warnings_post_shutdown { my ($server_socket)= @_; my $testname_hash= { }; + my $report= ''; foreach my $mysqld ( mysqlds()) { - my $testlist= extract_warning_lines($mysqld->value('#log-error')); + my ($testlist, $match_lines)= + extract_warning_lines($mysqld->value('#log-error')); $testname_hash->{$_}= 1 for @$testlist; + $report.= join('', @$match_lines); } my @warning_tests= keys(%$testname_hash); if (@warning_tests) { my $fake_test= My::Test->new(testnames => \@warning_tests); + $fake_test->{'warnings'}= $report; $fake_test->write_test($server_socket, 'WARNINGS'); } } @@ -4343,6 +4483,7 @@ sub mysqld_stop { mtr_init_args(\$args); mtr_add_arg($args, "--no-defaults"); + mtr_add_arg($args, "--character-sets-dir=%s", $mysqld->value('character-sets-dir')); mtr_add_arg($args, "--user=%s", $opt_user); mtr_add_arg($args, "--password="); mtr_add_arg($args, "--port=%d", $mysqld->value('port')); @@ -4392,7 +4533,7 @@ sub mysqld_arguments ($$$) { if (!using_extern() and $mysql_version_id >= 50106 ) { - # Turn on logging to bothe tables and file + # Turn on logging to file and tables mtr_add_arg($args, "%s--log-output=table,file"); } @@ -4555,7 +4696,8 @@ sub mysqld_start ($$) { $opt_start_timeout, $mysqld->{'proc'})) { - mtr_error("Failed to start mysqld $mysqld->name()"); + my $mname= $mysqld->name(); + mtr_error("Failed to start mysqld $mname with command $exe"); } # Remember options used when starting @@ -4566,11 +4708,12 @@ sub mysqld_start ($$) { sub stop_all_servers () { + my $shutdown_timeout = $_[0] or 0; mtr_verbose("Stopping all servers..."); # Kill all started servers - My::SafeProcess::shutdown(0, # shutdown timeout 0 => kill + My::SafeProcess::shutdown($shutdown_timeout, started(all_servers())); # Remove pidfiles @@ -4940,7 +5083,8 @@ sub start_servers($) { my $logfile= $mysqld->value('#log-error'); if ( defined $logfile and -f $logfile ) { - $tinfo->{logfile}= mtr_fromfile($logfile); + my @srv_lines= extract_server_log($logfile, $tinfo->{name}); + $tinfo->{logfile}= "Server log is:\n" . join ("", @srv_lines); } else { @@ -5369,7 +5513,6 @@ sub valgrind_arguments { else { mtr_add_arg($args, "--tool=memcheck"); # From >= 2.1.2 needs this option - mtr_add_arg($args, "--alignment=8"); mtr_add_arg($args, "--leak-check=yes"); mtr_add_arg($args, "--num-callers=16"); mtr_add_arg($args, "--suppressions=%s/valgrind.supp", $glob_mysql_test_dir) @@ -5402,6 +5545,8 @@ sub usage ($) { if ( $message ) { print STDERR "$message\n"; + print STDERR "For full list of options, use $0 --help\n"; + exit; } print <<HERE; @@ -5468,7 +5613,7 @@ Options to control what test suites or cases to run staging-run Run a limited number of tests (no slow tests). Used for running staging trees with valgrind. enable-disabled Run also tests marked as disabled - print_testcases Don't run the tests but print details about all the + print-testcases Don't run the tests but print details about all the selected tests, in the order they would be run. Options that specify ports @@ -5558,12 +5703,15 @@ Misc options servers to exit before finishing the process fast Run as fast as possible, dont't wait for servers to shutdown etc. - parallel=N Run tests in N parallel threads (default=1) + parallel=N Run tests in N parallel threads (default 1) Use parallel=auto for auto-setting of N repeat=N Run each test N number of times - retry=N Retry tests that fail N times, limit number of failures - to $opt_retry_failure - retry-failure=N Limit number of retries for a failed test + retry=N Retry tests that fail up to N times (default $opt_retry). + Retries are also limited by the maximum number of + failures before stopping, set with the --retry-failure + option + retry-failure=N When using the --retry option to retry failed tests, + stop when N failures have occured (default $opt_retry_failure) reorder Reorder tests to get fewer server restarts help Get this help text |