Go - Sending simultaneous emails through goroutines times out - connection reset by peer - email

I have 50 goroutines that start with a channel. Then, the process forever loops reading 100 database records at a time and sleeping for 10 seconds in between database calls. As it loops through the 100 email records to send, it passes each record through the channel to one of the 50 worker goroutines, who then sends the email. The problem is, after it goes through about 1000 emails, I start getting errors like this:
gomail: could not send email 1: read tcp 10.2.30.25:56708->216.###.##.###:25: read: connection reset by peer
I have to send out about 50k emails per day. What do you recommend? Here's the main code that processes the email queue and passes each record to the worker through the channel:
func main() {
MaxWorkers := 50
println("Creating: " + strconv.Itoa(MaxWorkers) + " workers..")
batchChannel := make(chan EmailQueue.EmailQueueObj)
for i := 0; i < MaxWorkers; i++ {
go startWorker(batchChannel)
}
for {
println("Getting queue..")
data, _ := EmailQueue.GetQueue() //returns 100 database records
println("Reading through " + strconv.Itoa(len(data)) + " records..")
for _, element := range data {
batchChannel <- element
}
time.Sleep(10 * time.Second)
}
}
func startWorker(channel chan EmailQueue.EmailQueueObj) {
var s gomail.SendCloser
var err error
open := false
for obj := range channel {
if !open {
s, err = dialer.Dial()
if err != nil {
fmt.Println(err.Error())
return
} else {
sendEmail(obj, &s)
}
} else {
sendEmail(obj, &s)
}
open = true
}
s.Close()
}
func sendEmail(obj EmailQueue.EmailQueueObj, s *gomail.SendCloser) {
m := gomail.NewMessage()
m.SetHeader("From", "example#example.com")
m.SetHeader("To", obj.Recipient)
m.SetHeader("Subject", obj.Subject.String)
m.SetBody("text/html", obj.HTML.String)
// Send the email
response := ""
status := ""
if err := gomail.Send(*s, m); err != nil {
response = err.Error()
status = "error"
} else {
response = "Email sent"
status = "sent"
}
m.Reset()
return
}
I am using the library gomail to send the emails. I am open to anything, even a new library or method to send these emails. But, what I'm doing currently is not working. Any help is greatly appreciated!

Related

When to use selector.AddReceive, selector.Select

Would appreciate some clarification on when I should use selector.AddReceive and selector.Select. This might not be a Cadence problem, but perhaps I'm missing some knowledge with regards to Golang.
For selector.Select I think the basic idea is that we wait for the next output from a channel. Not entirely sure what selector.AddRecieve does.
For example, in the cadence examples, local_activity link and pasted below:
func signalHandlingWorkflow(ctx workflow.Context) error {
logger := workflow.GetLogger(ctx)
ch := workflow.GetSignalChannel(ctx, SignalName)
for {
var signal string
if more := ch.Receive(ctx, &signal); !more {
logger.Info("Signal channel closed")
return cadence.NewCustomError("signal_channel_closed")
}
logger.Info("Signal received.", zap.String("signal", signal))
if signal == "exit" {
break
}
cwo := workflow.ChildWorkflowOptions{
ExecutionStartToCloseTimeout: time.Minute,
// TaskStartToCloseTimeout must be larger than all local activity execution time, because DecisionTask won't
// return until all local activities completed.
TaskStartToCloseTimeout: time.Second * 30,
}
childCtx := workflow.WithChildOptions(ctx, cwo)
var processResult string
err := workflow.ExecuteChildWorkflow(childCtx, processingWorkflow, signal).Get(childCtx, &processResult)
if err != nil {
return err
}
logger.Sugar().Infof("Processed signal: %v, result: %v", signal, processResult)
}
return nil
}
We don't use any selector.AddReceive
But, in the example here, where it uses signal channels as well: Changing the uber cadence sleeptime based on external input
I'll also paste the code here
func SampleTimerWorkflow(ctx workflow.Context, timerDelay time.Duration) error
{
logger := workflow.GetLogger(ctx)
resetCh := workflow.GetSignalChannel(ctx, "reset")
timerFired := false
delay := timerDelay
for ;!timerFired; {
selector := workflow.NewSelector(ctx)
logger.Sugar().Infof("Setting up a timer to fire after: %v", delay)
timerCancelCtx, cancelTimerHandler := workflow.WithCancel(ctx)
timerFuture := workflow.NewTimer(timerCancelCtx, delay)
selector.AddFuture(timerFuture, func(f workflow.Future) {
logger.Info("Timer Fired.")
timerFired = true
})
selector.AddReceive(resetCh, func(c workflow.Channel, more bool) {
logger.Info("Reset signal received.")
logger.Info("Cancel outstanding timer.")
cancelTimerHandler()
var t int
c.Receive(ctx, &t)
logger.Sugar().Infof("Reset delay: %v seconds", t)
delay = time.Second * time.Duration(t)
})
logger.Info("Waiting for timer to fire.")
selector.Select(ctx)
}
workflow.GetLogger(ctx).Info("Workflow completed.")
return nil
}
You can see there is selector.AddReceive, I'm not entirely sure what the purpose is or when I should use it.
I am trying to send a signal to my workflow that allows me to extend an expiration time. Meaning, it would delay the call of an ExpirationActivity
And when following this example (combined with my current code), as soon as I send the signal to reset, it seems that timerFired gets set immediately to true.
My current code is the below (I've taken out some irrelevant if statements), and previously, I was using only one instance of selector.Select, but somewhere my code wasn't acting properly.
func Workflow(ctx workflow.Context) (string, error) {
// local state per bonus workflow
bonusAcceptanceState := pending
logger := workflow.GetLogger(ctx).Sugar()
logger.Info("Bonus workflow started")
timerCreated := false
timerFired := false
delay := timerDelay
// To query state in Cadence GUI
err := workflow.SetQueryHandler(ctx, "bonusAcceptanceState", func(input []byte) (string, error) {
return bonusAcceptanceState, nil
})
if err != nil {
logger.Info("SetQueryHandler failed: " + err.Error())
return "", err
}
info := workflow.GetInfo(ctx)
executionTimeout := time.Duration(info.ExecutionStartToCloseTimeoutSeconds) * time.Second
// decisionTimeout := time.Duration(info.TaskStartToCloseTimeoutSeconds) * time.Second
decisionTimeout := time.Duration(info.ExecutionStartToCloseTimeoutSeconds) * time.Second
maxRetryTime := executionTimeout // retry for the entire time
retryPolicy := &cadence.RetryPolicy{
InitialInterval: time.Second,
BackoffCoefficient: 2,
MaximumInterval: executionTimeout,
ExpirationInterval: maxRetryTime,
MaximumAttempts: 0, // unlimited, bound by maxRetryTime
NonRetriableErrorReasons: []string{},
}
ao := workflow.ActivityOptions{
TaskList: taskList,
ScheduleToStartTimeout: executionTimeout, // time until a task has to be picked up by a worker
ScheduleToCloseTimeout: executionTimeout, // total execution timeout
StartToCloseTimeout: decisionTimeout, // time that a worker can take to process a task
RetryPolicy: retryPolicy,
}
ctx = workflow.WithActivityOptions(ctx, ao)
selector := workflow.NewSelector(ctx)
timerCancelCtx, cancelTimerHandler := workflow.WithCancel(ctx)
var signal *singalType
for {
signalChan := workflow.GetSignalChannel(ctx, signalName)
// resetCh := workflow.GetSignalChannel(ctx, "reset")
selector.AddReceive(signalChan, func(c workflow.Channel, more bool) {
c.Receive(ctx, &signal)
})
selector.Select(ctx)
if signal.Type == "exit" {
return "", nil
}
// We can check the age and return an appropriate response
if signal.Type == "ACCEPT" {
if bonusAcceptanceState == pending {
logger.Info("Bonus Accepted")
bonusAcceptanceState = accepted
var status string
future := workflow.ExecuteActivity(ctx, AcceptActivity)
if err := future.Get(ctx, &status); err != nil {
logger.Errorw("Activity failed", "error", err)
}
// Start expiration timer
if !timerCreated {
timerCreated = true
timerFuture := workflow.NewTimer(timerCancelCtx, delay)
selector.AddFuture(timerFuture, func(f workflow.Future) {
logger.Info("Timer Fired.")
timerFired = true
})
}
}
}
if signal.Type == "ROLLOVER_1X" && bonusAcceptanceState == accepted {
var status string
future := workflow.ExecuteActivity(ctx, Rollover1x)
if err := future.Get(ctx, &status); err != nil {
logger.Errorw("Activity failed", "error", err)
}
selector.Select(ctx)
}
if signal.Type == "ROLLOVER_COMPLETE" && bonusAcceptanceState == accepted {
var status string
future := workflow.ExecuteActivity(ctx, RolloverComplete)
if err := future.Get(ctx, &status); err != nil {
logger.Errorw("Activity failed", "error", err)
return "", err
}
// Workflow is terminated on return result
return status, nil
}
for; !timerFired && bonusAcceptanceState == accepted && signal.Type == "RESET" {
cancelTimerHandler()
i, err := strconv.Atoi(signal.Value)
if err != nil {
logger.Infow("error in converting")
}
logger.Infof("Reset delay: %v seconds", i)
delay = time.Minute * time.Duration(i)
timerFuture := workflow.NewTimer(timerCancelCtx, delay)
selector.AddFuture(timerFuture, func(f workflow.Future) {
logger.Info("Timer Fired.")
timerFired = true
})
selector.Select(ctx)
}
if timerFired {
var status string
future := workflow.ExecuteActivity(ctx, ExpirationActivity)
if err := future.Get(ctx, &status); err != nil {
logger.Errorw("Activity failed", "error", err)
}
return status, nil
}
}
}
TL;DR:
You will only use selector.AddReceive when you need to let a selector to listen on a channel, like in your 2nd code snippet. If you only need to process signals from a channel directly without selector, then you don't need to use it.
selector.Select is to let the code wait for some events to happen. Because you don't want to use busy looping to wait.
More details on when to use them
Essentially, this is exactly the same concept as Golang select statement. Golang select allows you to wait for timers and channels. Except that Golang doesn't have selector.Select() simply because it's baked into the language itself, but Cadence is a library.
So same as in golang, you don't have to use select statement to use timer or channel. You only need it when you have to write some code to listen on multiple sources of event.
For example, if you have two channels, you want to write some common logic to process these two channels, e.g increase a counter. This counter doesn't belong to any of the channels. It's a common counter. Then using a selector will looks nice.
chA := workflow.GetSignalChannel(ctx, SignalNameA)
chB := workflow.GetSignalChannel(ctx, SignalNameB)
counter := 0
selector.AddReceive(chA)
selector.AddReceive(chB)
For {
selector.Select()
counter += 1
}
The workflow code with selector looks very similar to this in Golang:
counter := 0
for {
select {
case _ := <- chA:
counter += 1
case _ := <- chB:
counter += 1
}
}
Otherwise you may have to use two goroutines to listen on each channel, and do the counting. The golang code looks like this:
counter := 0
go func(){
for{
_ := <- chA
counter += 1
}
}()
go func(){
for{
_ := <- chB
counter += 1
}
}()
This could be a problem of race condition. Unless the counter is well implemented as thread-safe.
And in Cadence workflow code, it's something like this:
chA := workflow.GetSignalChannel(ctx, SignalNameA)
chB := workflow.GetSignalChannel(ctx, SignalNameB)
counter := 0
Workflow.Go(ctx){
for{
chA.Receive(ctx,nil)
counter +=1
}
}
Workflow.Go(ctx){
for{
chB.Receive(ctx,nil)
counter +=1
}
}
However, there is no such race condition in Cadence, because Cadence's coroutine(started byWorkflow.Go()) is not really concurrency. Both the two workflow code above should work perfectly.
But Cadence still provide this selector same as Golang, mostly because the 1st one is more natural to write code.
check the future return result
selector.AddFuture(timerFuture, func(f workflow.Future) {
err := f.Get(ctx, nil)
if err == nil {
logger.Info("Timer Fired.")
timerFired = true
}
})
ref: https://github.com/uber-go/cadence-client/blob/0256258b905b677f2f38fcacfbda43398d236309/workflow/deterministic_wrappers.go#L128-L129

How to read all data from a TCP socket server and execute an operation afterwards

After spending many hours on this. I can't find a way to read all the data coming from a TCP socket server and then make an operation, as I can't find a way to break the loop.
The socket server sends texts containing a lot of lines finishing with "\n". The client should be able to read all those lines and make a POST request with all the data but the loop always hangs out and there is not a way to break it. Then, it will continue waiting for more data, so a stop condition could be a three seconds timeout.
I have tried different solutions (Scanner, ReadString, ReadLine, ReadAll) but it always hangs out and the loop won't ever finish.
The last line in the code is never printed.
conn, err := net.Dial("tcp", "127.0.0.1:15000")
reader := bufio.NewReader(conn)
message := ""
for {
line, err := reader.ReadString('\n')
if err == io.EOF {
break
}
message += line
}
log.Println(message)
If your only option is to read lines until a timeout, you can set a read deadline on the connection after the first read completes. You can then intercept the timeout error, and convert it to an EOF for the buffered reader to correctly interpret your intent.
type timeoutReader struct {
net.Conn
once sync.Once
}
func (r *timeoutReader) Read(b []byte) (int, error) {
n, err := r.Conn.Read(b)
// Set a read deadline only after the first Read completes
r.once.Do(func() {
r.Conn.SetReadDeadline(time.Now().Add(3 * time.Second))
})
// If we got a timeout, treat it as an io.EOF so the bufio.Scanner handles
// the error as if it was the normal end of the stream.
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
return n, io.EOF
}
return n, err
}
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:15000")
if err != nil {
log.Fatal(err)
}
scanner := bufio.NewScanner(&timeoutReader{Conn: conn})
message := ""
for scanner.Scan() {
message += scanner.Text()
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
log.Println(message)
}
If the criteria is a timeout of 3 seconds after the first line is received, the solution is to close the socket 3 seconds after the first line is received.
var firstLineReceived bool
conn, err := net.Dial("tcp", "127.0.0.1:15000")
reader := bufio.NewReader(conn)
message := ""
for {
line, err := reader.ReadString('\n')
if err == io.EOF {
break
}
message += line
if !firstLineReceived {
firstLineReceived = true
go func(){
time.Sleep(3*time.Second)
conn.Close()
}()
}
}
log.Println(message)

How to set ToS field in IP header for a TCP connection using Golang

I am trying to create a TCP server and client using Golang where I am able to set the Type of Service field in the IP header in order to prioritise different traffic flows.
The client and servers are able to communicate but I can not figure out how to set the ToS field.
I have tried using the ipv4 Golang package with the method described here: https://godoc.org/golang.org/x/net/ipv4#NewConn
A simplified server example:
func main () {
ln, err := net.Listen("tcp4", "192.168.0.20:1024")
if err != nil {
// error handling
}
defer ln.Close()
for {
c, err := ln.Accept()
if err != nil {
// error handling
}
go func(c net.Conn) {
defer c.Close()
if err := ipv4.NewConn(c).SetTOS(0x28); err != nil {
fmt.Println("Error: ", err.Error())
}
}(c)
}
And the corresponding client (also simplified)
func main () {
conn, err := net.Dial("tcp4", "192.168.0.20:1024")
if err != nil {
fmt.Println(err)
}
for {
writer := bufio.NewWriter(conn)
// Create "packet"
Data := make([]byte, 1200)
endLine := "\r\n"
//Set packetLength
length := strconv.FormatInt(int64(1200), 10)
copy(Data[0:], length)
//Set ID
idString := strconv.FormatInt(int64(1), 10)
if strings.Contains(idString, "\r") || strings.Contains(idString, "\n") || strings.Contains(idString, "\r\n") {
fmt.Println("This is gonna result in an error in the id string.")
}
idbuf := []byte(idString)
copy(Data[15:], idbuf)
//Set timestamp
timestamp0 := time.Now().UnixNano()
timestampString := strconv.FormatInt(timestamp0, 10)
if strings.Contains(timestampString, "\r") || strings.Contains(timestampString, "\n") || strings.Contains(timestampString, "\r\n") {
fmt.Println("This is gonna result in an error in the timestamp string.")
}
buf := []byte(timestampString)
copy(Data[50:], buf)
copy(Data[int(1200)-2:], endLine)
if len(Data) != int(1200) {
fmt.Println("This is also gonna be an error. Length is: ", len(Data))
}
//Send the data and flush the writer
writer.Write(Data)
writer.Flush()
}
//time.Sleep(1*time.Nanosecond)
}
I have also tried creating my own dialer with a control function that passes a syscall in order to set the socket like this:
dialer := &net.Dialer{
Timeout: 5 * time.Second,
Deadline: time.Time{},
LocalAddr: tcpAddr,
DualStack: false,
FallbackDelay: 0,
KeepAlive: 0,
Resolver: nil,
Control: highPrio,
}
func highPrio(network, address string, c syscall.RawConn) error {
return c.Control(func(fd uintptr) {
// set the socket options
err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_IP, syscall.IP_TOS, 128)
if err != nil {
log.Println("setsocketopt: ", err)
}
})
I am verifying that it does not work by inspecting the traffic with Wireshark and am using Windows 10 Pro as my OS.
I am try you ToS set method at Dial() with golang 1.15.5 and its worked:
dialer := net.Dialer{
Timeout: this.TcpWaitConnectTimeout,
}
dialer.Control = func(network, address string, c syscall.RawConn) error {
var err error
c.Control(func(fd uintptr) {
err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TOS, 0x80)
})
return err
}
c, err := dialer.Dial("tcp", this.serverAddr)
tcpdump show me right ToS

How to deal with sticky tcp packet in go?

I have a tcp server and a client, the server does the following
func providerCallback(conn net.Conn) {
reader := bufio.NewReader(conn)
var err error
for {
lenbyte, _ := reader.Peek(4)
reader.Discard(4)
slen := int(binary.BigEndian.Uint32(lenbyte))
data, err = reader.Peek(slen)
process(data)
reader.Discard(slen)
}
}
The client seems to send packet faster than process can deal with, therefore I'd like to buffer the requests in bufio and process later.
However, as the size of bufio is fixed(4096, even though I can increase it, it is still fixed), which means I can't manually Reset it because there might be a packet cutting of in the end of bufio, as follows
|normal data... [First 20 bytes of packet P] | [the rest of packet P]
|------------------- size of bufio ------------------|
How can I splice packet that is cut off, and reuse the bufio for later packets?
For example,
import (
"bufio"
"encoding/binary"
"io"
"net"
)
func providerCallback(conn net.Conn) error {
rdr := bufio.NewReader(conn)
data := make([]byte, 0, 4*1024)
for {
n, err := io.ReadFull(rdr, data[:4])
data = data[:n]
if err != nil {
if err == io.EOF {
break
}
return err
}
dataLen := binary.BigEndian.Uint32(data)
if uint64(dataLen) > uint64(cap(data)) {
data = make([]byte, 0, dataLen)
}
n, err = io.ReadFull(rdr, data[:dataLen])
data = data[:n]
if err != nil {
return err
}
process(data)
}
return nil
}
func process([]byte) {}

Golang - net.Conn infinite loop on the same message

I'm pretty news in Golang and only use sockets in this langage for 2days. However, I'm not sure to understand something. I know in C, I used select() to know who wrote etc, but here, no one is writing until one send a message. After this message sent, my dialTCP uses it endlessly.
I think I missunderstood something about close() but I'm not sure it comes from here.. there is my code:
package dial
import (
"errors"
"encoding/json"
m "models"
"net"
"net/http"
"time"
"server"
)
type DialTCP struct {}
// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
// connections. It's used by ListenAndServe and ListenAndServeTLS so
// dead TCP connections (e.g. closing laptop mid-download) eventually
// go away.
type tcpKeepAliveListener struct {
*net.TCPListener
}
func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
tc, err := ln.AcceptTCP()
if err != nil {
return
}
tc.SetKeepAlive(true)
tc.SetKeepAlivePeriod(3 * time.Minute)
return tc, nil
}
func (dialTCP *DialTCP) ListenAndServe(addr string) error {
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return dialTCP.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}
func (dialTCP *DialTCP) Serve(l net.Listener) error {
defer l.Close()
for {
conn, e := l.Accept()
if e != nil {
return e
}
// you want to create server_conn here with buffers, channels and stuff
// to use async thread safe read/write from it
go dialTCP.serve_conn(conn)
}
}
func (dialTCP *DialTCP) serve_conn(conn net.Conn) error {
// var buf [512]byte
dec := json.NewDecoder(conn)
//read 1st message he sent, should be token to connect
var auth m.DialAuthentication
dec.Decode(&auth)
user := m.User{
UUID: auth.UUID,
}
ok, sb := server.IsConnected(user)
if ok == false {
json.NewEncoder(conn).Encode(sb)
return errors.New("User isn't connected.")
} else {
user.Conn = conn
}
//defer conn.Close()
var message m.DialMessageContainer
for {
dec.Decode(&message)
switch message.Type {
case ".....":
/* ....(message, user)
case "....":
....(message, user)
// case "...":*/
default:
json.NewEncoder(conn).Encode(m.StatusBack{Description: "Bad entry.", StatusId: http.StatusNotAcceptable})
}
//defer conn.Close()
}
}
I think everything is good before serv_conn(), but the error should comes from inside the for. I tried lot of things, but this for{} in Golang... Why does it have not any params/var such as C/C++/C#/Java?
for (int i = 0; i < 10; i++) {}
I'm lost about the closing of the Conn as well, so I continue to read tutorial, post and doc about how to use it.. Days pass whitout find anything