JohnnyD
JohnnyD

Reputation: 477

Go buffered channel falls through select

I have 2 buffered channels servicing inbound requests:

rabbitQ = make(chan map[string]interface{}, 1000)
sqsQ = make (chan map[string]interface{}, 1000)

My dispatcher function looks like this:

func dispatchMessage(params map[string]interface{}) {

    if !shouldFailoverToSQS {
        select {
        case rabbitQ <- params:
            sentToRabbitMQ++
        case sqsQ <- params:
            sentToSQS++
        default:
            log.Error("Failed to dispatch mesaage to either RabbitMQ or SQS")
        }
    } else {
        sqsQ <- params
    }

}

I would expect that messages would always be sent to rabbitQ unless the buffer was at capacity but I'm finding that the call is falling through and sending messages to sqsQ about half the time. This is not what I want - I only want to send to sqsQ if rabbitQ is full.

How can I enforce this?

Upvotes: 1

Views: 522

Answers (3)

gipsy
gipsy

Reputation: 3859

func dispatchMessage(params map[string]interface{}) {

    if !shouldFailoverToSQS {
        select {
        case rabbitQ <- params:
            sentToRabbitMQ++
        case <-time.After(time.Millisecond * 10.0): // rabbit is blocked and 10 milli sec passed.
            select {
            case sqsQ <- params:
                sentToSQS++  
            default:
            log.Error("Failed to dispatch mesaage to either RabbitMQ or SQS")
            }
        }
    } else {
        sqsQ <- params
    }

}

Upvotes: 0

william.taylor.09
william.taylor.09

Reputation: 2215

For a concise answer (it looks like you already have it from your answer + the thread comment), your select will choose at random. Per the docs:

If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection. Otherwise, if there is a default case, that case is chosen. If there is no default case, the "select" statement blocks until at least one of the communications can proceed.

In addition, I figured I would provide you with an answer that doesn't use selects at all. Using len, you can determine how many values are queued up in a channel. Sure it's no different than what you're doing now, but I figured I would offer something different:

const MaxChan = 1000
func dispatchMessage(params map[string]interface{}) {
    if !shouldFailoverToSQS {
        if len(rabbitQ) < MaxChan {
            sentToRabbitMQ++
        } else if len(sqsQ) < MaxChan {
            sentToSQS++
        } else {
            log.Error("Failed to dispatch mesaage to either RabbitMQ or SQS")
        }
    } else {
        if len(sqsQ) < MaxChan {
            sentToRabbitMQ++
        } else if len(rabbitQ) < MaxChan {
            sentToSQS++
        } else {
            log.Error("Failed to dispatch mesaage to either RabbitMQ or SQS")
        }
    }
}

Supporting docs: https://golang.org/ref/spec#Select_statements

Upvotes: 0

JohnnyD
JohnnyD

Reputation: 477

Per Voker's comment, this is what I came up with:

func dispatchMessage(params map[string]interface{}) {

    //log.Debugf("Failover: %t, Len: %d", shouldFailoverToSQS, len(rabbitQ))

    if !shouldFailoverToSQS {
        select {
        case rabbitQ <- params:
            sentToRabbitMQ++
        default:
            select {
            case sqsQ <- params:
                sentToSQS++
            default:
                log.Error("Failed to dispatch mesaage to either RabbitMQ or SQS")
            }
        }
    } else {
        select {
        case sqsQ <- params:
            sentToSQS++
        default:
            log.Error("Failed to dispatch mesaage to either RabbitMQ or SQS")
        }
    }

}

Upvotes: 1

Related Questions