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

[PATCH RFC3 10/13] send-email: Add --sleep for email throttling

From
Michael Witten <mfwitten@gmail.com>
Date
Apr 13, 2009, 18:23 UTC
Message-ID
<1239647037-15381-11-git-send-email-mfwitten@gmail.com>
In-Reply-To
<1239647037-15381-10-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 5f7d640..236e578 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -178,6 +178,36 @@ Automating
 	cc list. Default is the value of 'sendemail.signedoffbycc' configuration
 	value; if that is unspecified, default to --signed-off-by-cc.
 
+--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 any two messages.
++
+*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.
+
 --suppress-cc=<category>::
 	Specify an additional category of recipients to suppress the
 	auto-cc of:
diff --git a/git-send-email.perl b/git-send-email.perl
index 0ff72f6..a394663 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -72,6 +72,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,
@@ -189,7 +190,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);
@@ -205,6 +206,7 @@ my %config_bool_settings = (
 );
 
 my %config_settings = (
+	"sleep" => \$sleep,
 	"smtpserver" => \$smtp_server,
 	"smtpserverport" => \$smtp_server_port,
 	"smtpuser" => \$smtp_authuser,
@@ -257,6 +259,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=s" => \$smtp_server_port,
 	"smtp-user=s" => \$smtp_authuser,
@@ -329,6 +332,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);
 
@@ -1033,8 +1073,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;
@@ -1143,7 +1187,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 = $_;
@@ -1190,7 +1234,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 11 of 39 in “I hope this will do it!”
  1. I hope this will do it!Michael Witten, Apr 13, 2009
  2. 01/13 Docs: send-email: Put options back into alphabetical orderMichael Witten, Apr 13, 2009
  3. 02/13 Docs: send-email: Refer to CONFIGURATION section for sendemail.multieditMichael Witten, Apr 13, 2009
  4. 03/13 Docs: send-email: Remove superfluous information in CONFIGURATIONMichael Witten, Apr 13, 2009
  5. 04/13 Docs: send-email: --smtp-server-port can take symbolic portsMichael Witten, Apr 13, 2009
  6. 05/13 send-email: Cleanup the usage text and docs a bitMichael Witten, Apr 13, 2009
  7. 06/13 send-email: Handle "GIT:" rather than "GIT: " during --composeMichael Witten, Apr 13, 2009
  8. 07/13 send-email: 'References:' should only reference what is sentMichael Witten, Apr 13, 2009
  9. 08/13 send-email: Remove superfluous `my $editor = ...'Michael Witten, Apr 13, 2009
  10. 09/13 send-email: Remove horrible mix of tabs and spacesMichael Witten, Apr 13, 2009
  11. 10/13 send-email: Add --sleep for email throttlingMichael Witten, Apr 13, 2009
  12. 11/13 send-email: Minor cleanup of $smtp_server usage and send_message()Michael Witten, Apr 13, 2009
  13. 12/13 send-email: --compose takes optional argument to existing fileMichael Witten, Apr 13, 2009
  14. 13/13 send-email: --compose always includes a 'GIT: ' prefixed list of patch subjectsMichael Witten, Apr 13, 2009
  15. Junio C HamanoApr 13, 2009
  16. Michael WittenApr 13, 2009
  17. Andreas EricssonApr 14, 2009
  18. Junio C HamanoApr 14, 2009
  19. Andreas EricssonApr 14, 2009
  20. Michael WittenApr 14, 2009
  21. Andreas EricssonApr 14, 2009
  22. Stephen BoydApr 13, 2009
  23. Michael WittenApr 14, 2009
  24. Michael WittenApr 14, 2009
  25. Björn SteinbrinkApr 14, 2009
  26. Junio C HamanoApr 14, 2009
  27. Junio C HamanoApr 13, 2009
  28. Michael WittenApr 13, 2009
  29. Junio C HamanoApr 14, 2009
  30. Michael WittenApr 14, 2009
  31. Junio C HamanoApr 14, 2009
  32. Michael WittenApr 14, 2009
  33. Junio C HamanoApr 13, 2009
  34. Michael WittenApr 13, 2009
  35. Michael WittenApr 13, 2009
  36. Junio C HamanoApr 14, 2009
  37. Michael WittenApr 14, 2009
  38. Junio C HamanoApr 14, 2009
  39. Michael WittenApr 14, 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.