polling

package
v0.0.0-...-dc23390 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 23, 2018 License: BSD-3-Clause Imports: 4 Imported by: 0

Documentation

Overview

Package polling provides cost-effective ways to reduce cost of polling resources. For example: instead of busy-waiting on a service endpoint that may or may not have a payload to process, wrapping said call as a Receiver.Receive() call and using a PollingChannel to wrap said receiver is provides a better poller that may use contingent on Bernoulli trials cyclic exponential backoff between calls.

For example,

polling.NewPoller(receiver, 0.95, time.Millisecond, time.Minute)

creates a poller that keeps sending along (via the poller's send-only channel) payloads received from the specified receiver as long as they keep arriving. When the receiver is empty-handed (denoted by a flag on the Receive method's return value), the pollers runs a Bernoulli trial with a probability of 0.95 to determine whether to back off (95% of the time), or call Receive() again immediately (a costly busy-waiting; some IaaS providers, like AWS, may charge by the call). When backing off, the poller waits between empty-handed calls; it starts with a 1ms delay (seed), then 2ms, then 4ms, etc. The exponential delay is capped at 1 minute (cap), after which it starts from the seed again.

The contingent nature of the cyclic exponential backoff between calls (thanks to Bernoulli sampling) allows the poller to break out of the cycle to check for new payloads – intermittently, which is useful when the delay intervals are long.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Poller

type Poller interface {
	// Channel gets the underlying channel that wraps the resource being polled.
	// By default, sends block until the other side (the receiver) is ready.
	Channel() <-chan interface{}

	// GetName returns name of the poller.
	GetName() string

	// Start starts polling for new payloads to arrive on the receiving end.
	// This method is non-idempotent.
	Start()

	// Stop signals the poller to stop polling; it also closes the channel.
	// This method is non-idempotent.
	Stop()
}

Poller represents the resource being polled as a send-only channel. Ideally, this would be a generic type (see https://golang.org/doc/faq#generics).

Example
package main

import (
	"errors"
	"fmt"
	"math/rand"
	"time"
)

type counter struct{ count int }
type alwaysEmptyHandedReceiver counter
type neverEmptyHandedReceiver counter
type threeSidedDieReceiver counter

func (*alwaysEmptyHandedReceiver) Receive() (payload interface{}, found bool, err error) {
	return
}

func (receiver *neverEmptyHandedReceiver) Receive() (interface{}, bool, error) {
	receiver.count++
	return receiver.count, true, nil
}

func (receiver *threeSidedDieReceiver) Receive() (interface{}, bool, error) {
	receiver.count++
	cast := rand.Float64()
	if cast < 1.0/3 {
		return receiver.count, true, nil
	} else if cast < 2.0/3 {
		return receiver.count, false, nil
	} else {
		return nil, false, errors.New("failed successfully")
	}
}

func main() {
	rand.Seed(0) // to produce the same sequence of pseudo-random numbers every time
	receivers := []Receiver{&alwaysEmptyHandedReceiver{}, &neverEmptyHandedReceiver{}, &threeSidedDieReceiver{}}

	for r, receiver := range receivers {
		poller, err := NewBernoulliExponentialBackoffPoller(
			receiver, "test", 0.5, time.Nanosecond, time.Millisecond)
		if err != nil {
			panic(err)
		}
		payloads := []interface{}{}

		go poller.Start()

		for i := 0; r > 0 && i < 13; i++ {
			payloads = append(payloads, <-poller.Channel())
		}

		poller.Stop()
		_, receiving := <-poller.Channel()
		fmt.Printf("Stopped; is channel receiving from %T? %t\n", receiver, receiving)
		fmt.Println("Payloads received:", payloads)
		fmt.Println()
	}
}
Output:

Stopped; is channel receiving from *polling.alwaysEmptyHandedReceiver? false
Payloads received: []

Stopped; is channel receiving from *polling.neverEmptyHandedReceiver? false
Payloads received: [1 2 3 4 5 6 7 8 9 10 11 12 13]

Stopped; is channel receiving from *polling.threeSidedDieReceiver? false
Payloads received: [4 6 7 9 15 17 19 20 21 25 31 38 44]

func NewBernoulliExponentialBackoffPoller

func NewBernoulliExponentialBackoffPoller(
	receiver Receiver, entityName string, probability float64, seed, cap time.Duration) (Poller, error)

NewBernoulliExponentialBackoffPoller creates a polling channel that uses Bernoulli trials with cyclic exponential backoff between empty-handed calls.

type Receiver

type Receiver interface {
	// Receive receives from the resource being polled, returning
	// the received payload (if any), a flag to indicate whether or not
	// a payload was found, and an error if encountered.
	Receive() (interface{}, bool, error)
}

Receiver represents the simple act of receiving payloads (e.g., messages) from the resource being polled.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL