Skip to content
This repository has been archived by the owner on May 6, 2018. It is now read-only.

API: Create Mail queue (to be used by (*Email).Send or (*Email).Enqueue) #20

Closed
elimisteve opened this issue Feb 16, 2014 · 3 comments
Closed
Assignees

Comments

@elimisteve
Copy link
Member

Async email sending (infinite queue) --

var EmailQueue = make(chan *Email)

go func() {
    // Infinite loop
    for {
        // Blocking call preventing this loop from being a CPU burner
        email := <-EmailQueue

        // Could make this function a helper
        go func(e *Email) {
            err := e.Send()
            if err != nil {
                // Update EmailStatus with FAILED status (see #19)
                return
            }
            // Update EmailStatus with SENT
        }(email)
    }
}

What is an EmailStatus? See #19.

@elimisteve elimisteve added this to the MVP: Version 0.1 milestone Feb 16, 2014
@elimisteve
Copy link
Member Author

Naive version that can only send 1 email at a time --

var EmailQueue = make(chan *Email)

go func() {
    for {
        email := <-EmailQueue
        // Synchronous .Send prevents emails
        // from being added to queue; BAD
        err := email.Send()
        if err != nil {
            // FAILED
            continue
        }
        // SUCCESS
    }
}

Note also that this doesn't allow any new emails to be queued up while another email is sent, which makes this especially egregious. We could at least allow new emails to enter the queue by changing the first line from

var EmailQueue = make(chan *Email)

to

var EmailQueue = make(chan *Email, 10)

which would allow EmailQueue -- now a buffered channel with a capacity of 10 instead of a regular unbuffered channel with an implied capacity of 0 -- to contain up to 10 emails before channel sends (EmailQueue <- e) begin to block.

@elimisteve
Copy link
Member Author

Another option: effectively infinite queue (channel reads are immediate), but limit the number of email attempts at any given moment (perhaps to please Mandrill or whoever) using a buffered channel as a semaphore --

var EmailQueue = make(chan *Email)
var semaphore = make(chan bool, 10) // Can only send 10 at once

go func() {
    for {
        email := <-EmailQueue
        // Spawn goroutine immediately
        go func(e *Email) {
            // Will only block if 10 emails already being sent.
            // That is, will only block if 10 bools
            // are already in the `semaphore` channel
            semaphore <- true
            defer func() {
                // Drain value from channel to make room
                // for another email sender
                <-semaphore
            }()

            err := e.Send()
            if err != nil {
                // FAILED
                return
            }
            // SUCCESS
        }(email)
    }
}

Another semaphore example: search http://golang.org/doc/effective_go.html for MaxOutstanding.

@elimisteve
Copy link
Member Author

Notice how Go allows for all these various behaviors/dynamics all using just channels and goroutines? No special parallel async generator types or whatever, just simple, powerful primitives that can be used to quickly build anything.

ajvb added a commit that referenced this issue Feb 21, 2014
executes saving and senting independantly/async.

related to #17 #18 #19 #20
ajvb added a commit that referenced this issue Feb 21, 2014
Added async email.SaveAndSend() within the SendEmail
POST handler and changed the response to 'added
to the queue' rather than 'sent successfully';

related to #26 #20
@ajvb ajvb closed this as completed Feb 21, 2014
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants