De-duplicate email addresses in notify-mailer. (#4015)

Resolves #4003
This commit is contained in:
Jacob Hoffman-Andrews 2019-01-17 11:34:04 -08:00 committed by GitHub
parent 958cef1d1f
commit 281e2546f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 37 additions and 0 deletions

View File

@ -89,6 +89,21 @@ func (m *mailer) printStatus(to string, cur, total int, start time.Time) {
to, cur, total, completion, elapsed)
}
// uniq returns a slice of strings consisting of the input slice with all
// duplicates removed. It preserves the ordering of the input slice.
func uniq(input []string) []string {
var output []string
uniqMap := map[string]bool{}
for _, s := range input {
// Only append to the output items that have not been seen.
if _, ok := uniqMap[s]; !ok {
output = append(output, s)
}
uniqMap[s] = true
}
return output
}
func (m *mailer) run() error {
if err := m.ok(); err != nil {
return err
@ -99,6 +114,12 @@ func (m *mailer) run() error {
return err
}
lenBefore := len(destinations)
destinations = uniq(destinations)
m.log.Infof("Before de-duping: %d email addresses. After de-duping: %d email addresses",
lenBefore, len(destinations))
err = m.mailer.Connect()
if err != nil {
return err
@ -232,6 +253,12 @@ Similarly, the -end flag specifies which registration ID of the -toFile to end
processing at. In combination these can be used to process only a fixed number
of recipients at a time, and to resume mailing after early termination.
Notify-mailer will de-duplicate email addresses, but only within the range given
by the -start and -end arguments. For instance, if you split up an email job into
five batches using -start and -end, there's a possibility that a given email address
may receive up to five emails, if that email address is registered across multiple
accounts.
During mailing the -sleep argument is used to space out individual messages.
This can be used to ensure that the mailing happens at a steady pace with ample
opportunity for the operator to terminate early in the event of error. The

View File

@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"reflect"
"testing"
"time"
@ -15,6 +16,15 @@ import (
"github.com/letsencrypt/boulder/test"
)
func TestUniq(t *testing.T) {
input := []string{"c", "d", "c", "c", "e", "d", "e", "e"}
expected := []string{"c", "d", "e"}
output := uniq(input)
if !reflect.DeepEqual(output, expected) {
t.Errorf("Expected %s, got %s", expected, output)
}
}
func TestCheckpointIntervalOK(t *testing.T) {
// Test a number of intervals know to be OK, ensure that no error is
// produced when calling `ok()`.