git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH RFC 08/10] send-email: Add --sleep for email throttling

From
Michael Witten <mfwitten@gmail.com>
Date
Apr 11, 2009, 19:08 UTC
Message-ID
<1239476908-25944-8-git-send-email-mfwitten@gmail.com>
In-Reply-To
<1239476908-25944-7-git-send-email-mfwitten@gmail.com>

The --sleep option provides a means for specifying that there should be a certain number of seconds of delay after sending a certain number of emails; see Documentation/git-send-email.txt

Signed-off-by: Michael Witten <mfwitten@gmail.com>
---
 Documentation/git-send-email.txt |   30 +++++++++++++++
 git-send-email.perl              |   74 +++++++++++++++++++++++++++++++++++---
 2 files changed, 99 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 12845d6..f0c2e7b 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -168,6 +168,36 @@ Automating
 	entire patch series. Default is the value of the 'sendemail.chainreplyto'
 	configuration value; if that is unspecified, default to --chain-reply-to.
 
+--sleep=<seconds>[,<burst>]::
+	This option specfies that send-email should sleep for <seconds>
+	after sending <burst> messages as quickly as possible; <seconds>
+	should be an integer >= 0 and <burst> should be an integer >= 1.
+	This mode of operation attacks 2 problems: email throttling and
+	arrival disorder. Default is the value of the 'sendemail.sleep'
+	configuration variable, or '0' if that does not exist.
++
+By default, send-email tries to send one patch per email as quickly as
+possible. Unfortunately, some email services restrict a user by refusing
+to send more than some maximum number of email messages, M, in a given
+period of seconds, S. This can be troublesome if the patch series has
+more than M patches, because the server will ultimately refuse to send
+some of them. In this case, simply pass '--sleep=S,M' or '--sleep S,M'
+or set sendemail.sleep to 'S,M'.
++
+Moreover, the emails often arrive at the final destination out of order;
+though send-email manipulates the date fields and usually chains subsequent
+emails via the In-Reply-To headers, some mail viewers nevertheless insist
+on presenting them by order of arrival. This may be mitigated by using
+something like '--sleep 60' (the equivalent of '--sleep 60,1'), so that
+there is a 60 second delay between sending each patch.
++
+*Note*: Because of varying routes and batching schemes, there is no delay
+that can guarantee the correct arrival order. Obviously, one solution is to
+choose an obscenely large number, so be prepared to run send-email in the
+background. Of course, spreading emails across time makes it more likely
+that unrelated email messages arrive between patches. Therefore, send-email
+warns you if both --sleep and --no-chain-reply-to are used.
+
 --identity=<id>::
 	A configuration identity. When given, causes values in the
 	'sendemail.<id>' subsection to take precedence over
diff --git a/git-send-email.perl b/git-send-email.perl
index 4f0462f..c26a1b5 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -77,6 +77,7 @@ git send-email [options] <file | directory | rev-list options >
     --smtp-ssl                     * Deprecated. Use '--smtp-encryption ssl'.
 
   Automating:
+    --sleep      <secs>[,<burst>]  * Delay <secs> every <burst> email(s).
     --identity               <id>  * Use the sendemail.<id> options.
     --cc-cmd            <command>  * Email Cc: via `<command> \$patch_path`
     --suppress-cc      <category>  * author, self, sob, cc, cccmd, body,
@@ -194,7 +195,7 @@ sub do_edit {
 }
 
 # Variables with corresponding config settings
-my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd);
+my ($sleep, $thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd);
 my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_encryption);
 my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts);
 my ($validate, $confirm);
@@ -210,6 +211,7 @@ my %config_bool_settings = (
 );
 
 my %config_settings = (
+	"sleep" => \$sleep,
 	"smtpserver" => \$smtp_server,
 	"smtpserverport" => \$smtp_server_port,
 	"smtpuser" => \$smtp_authuser,
@@ -262,6 +264,7 @@ my $rc = GetOptions(
 	"cc=s" => \@initial_cc,
 	"bcc=s" => \@bcclist,
 	"chain-reply-to!" => \$chain_reply_to,
+	"sleep=s" => \$sleep,
 	"smtp-server=s" => \$smtp_server,
 	"smtp-server-port=i" => \$smtp_server_port,
 	"smtp-user=s" => \$smtp_authuser,
@@ -334,6 +337,43 @@ foreach my $setting (values %config_bool_settings) {
 	${$setting->[0]} = $setting->[1] unless (defined (${$setting->[0]}));
 }
 
+#### Parse input
+
+my ($sleep_seconds, $sleep_burst);
+
+if (defined $sleep) {{
+
+	unless ($chain_reply_to) {
+
+		print "Both --sleep and --no-chain-reply-to are in effect.\n";
+		print "Therefore, it is much more likely that unrelated\n";
+		print "email messages will appear between any 2 of your\n";
+		print "patches.\n\n";
+
+		$_ = ask(
+			"How to proceed? ([q]uit | --[s]leep | --no-[c]hain | [n]either | [b]oth): ",
+			valid_re => qr/^(?:b|s|c|n|q)/i,
+			default => 'b'
+		);
+
+		/^b/                                               or
+		/^s/ and $chain_reply_to = 1                       or
+		/^c/ and $sleep = undef, last                      or
+		/^n/ and $chain_reply_to = 1, $sleep = undef, last or
+		/^q/ and exit;
+	}
+
+	$sleep =~ /^(\d+)(?:,(\d+))?$/
+		or  print "Should be '--sleep=<seconds>[,<burst>]', but got '--sleep=\"$sleep\"'\n"
+		and exit;
+
+	# Explicitly convert to integers to avoid repeated conversion:
+	# (<burst> = 0 is not defined, but let's be nice and absorb it)
+
+	$sleep_seconds = 0+$1;
+	$sleep_burst   = $2 ? 0+$2 : 1;
+}}
+
 # 'default' encryption is none -- this only prevents a warning
 $smtp_encryption = '' unless (defined $smtp_encryption);
 
@@ -1041,8 +1081,12 @@ $references = $initial_reply_to || '';
 $subject = $initial_subject;
 $message_num = 0;
 
-foreach my $t (@files) {
-	open(F,"<",$t) or die "can't open file $t";
+my $burst_count = $sleep_burst;
+my $time_of_last_message;
+
+for (my $index = 0; $index < @files; $index++) {
+	my $file = $files[$index];
+	open(F,"<",$file) or die "can't open file $file";
 
 	my $author = undef;
 	my $author_encoding;
@@ -1151,7 +1195,7 @@ foreach my $t (@files) {
 	close F;
 
 	if (defined $cc_cmd && !$suppress_cc{'cccmd'}) {
-		open(F, "$cc_cmd $t |")
+		open(F, "$cc_cmd $file |")
 			or die "(cc-cmd) Could not execute '$cc_cmd'";
 		while(<F>) {
 			my $c = $_;
@@ -1198,7 +1242,27 @@ foreach my $t (@files) {
 
 	my $message_was_sent = send_message();
 
-	# set up for the next message
+	# Throttle the outgoing rate:
+
+	if ($sleep_seconds && $message_was_sent) {
+
+		$time_of_last_message = time;
+
+		unless (--$burst_count) {  # unless we can send more
+
+			$burst_count = $sleep_burst;
+
+			my $already_elapsed = time - $time_of_last_message;
+
+			if ($already_elapsed < $sleep_seconds && $index < $#files) {
+				my $this_long = $sleep_seconds - $already_elapsed;
+				while (($this_long -= sleep $this_long) > 0) {}
+			}
+		}
+	}
+
+	# set up for the next message:
+
 	if ($message_was_sent and $chain_reply_to || not defined $reply_to || length($reply_to) == 0) {
 		$reply_to = $message_id;
 		if (length $references > 0) {
-- 
1.6.2.2.479.g2aec
Previous: Michael WittenNext: Michael Witten
Message 8 of 23 in “Docs: send-email: Put options back into alphabetical order”
  1. 01/10 Docs: send-email: Put options back into alphabetical orderMichael Witten, Apr 11, 2009
  2. 02/10 Docs: send-email: Remove superfluous information in CONFIGURATIONMichael Witten, Apr 11, 2009
  3. 03/10 send-email: Cleanup the usage text and docs a bitMichael Witten, Apr 11, 2009
  4. 04/10 send-email: --smtp-server-port should take an integerMichael Witten, Apr 11, 2009
  5. 05/10 send-email: Handle "GIT:" rather than "GIT: " during --composeMichael Witten, Apr 11, 2009
  6. 06/10 send-email: References: should only reference what is actually sentMichael Witten, Apr 11, 2009
  7. 07/10 send-email: Remove horrible mix of tabs and spacesMichael Witten, Apr 11, 2009
  8. 08/10 send-email: Add --sleep for email throttlingMichael Witten, Apr 11, 2009
  9. 09/10 send-email: Minor cleanup of $smtp_server usage and send_message()Michael Witten, Apr 11, 2009
  10. 10/10 send-email: --compose takes optional argument to existing fileMichael Witten, Apr 11, 2009
  11. Sverre RabbelierApr 11, 2009
  12. Michael WittenApr 11, 2009
  13. Stephen BoydApr 11, 2009
  14. Michael WittenApr 11, 2009
  15. Jay SoffianApr 12, 2009
  16. Michael WittenApr 12, 2009
  17. Sverre RabbelierApr 11, 2009
  18. Michael WittenApr 11, 2009
  19. Miles BaderApr 13, 2009
  20. Michael WittenApr 13, 2009
  21. Stephen BoydApr 11, 2009
  22. Michael WittenApr 11, 2009
  23. Stephen BoydApr 11, 2009

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.