435c0d9fc8
This PR switches the Nomad repository from using govendor to Go modules for managing dependencies. Aspects of the Nomad workflow remain pretty much the same. The usual Makefile targets should continue to work as they always did. The API submodule simply defers to the parent Nomad version on the repository, keeping the semantics of API versioning that currently exists.
99 lines
2.3 KiB
Go
99 lines
2.3 KiB
Go
package statsd
|
|
|
|
import (
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
/*
|
|
UDSTimeout holds the default timeout for UDS socket writes, as they can get
|
|
blocking when the receiving buffer is full.
|
|
*/
|
|
const defaultUDSTimeout = 1 * time.Millisecond
|
|
|
|
// udsWriter is an internal class wrapping around management of UDS connection
|
|
type udsWriter struct {
|
|
// Address to send metrics to, needed to allow reconnection on error
|
|
addr net.Addr
|
|
// Established connection object, or nil if not connected yet
|
|
conn net.Conn
|
|
// write timeout
|
|
writeTimeout time.Duration
|
|
sync.RWMutex // used to lock conn / writer can replace it
|
|
}
|
|
|
|
// newUDSWriter returns a pointer to a new udsWriter given a socket file path as addr.
|
|
func newUDSWriter(addr string) (*udsWriter, error) {
|
|
udsAddr, err := net.ResolveUnixAddr("unixgram", addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Defer connection to first Write
|
|
writer := &udsWriter{addr: udsAddr, conn: nil, writeTimeout: defaultUDSTimeout}
|
|
return writer, nil
|
|
}
|
|
|
|
// SetWriteTimeout allows the user to set a custom write timeout
|
|
func (w *udsWriter) SetWriteTimeout(d time.Duration) error {
|
|
w.writeTimeout = d
|
|
return nil
|
|
}
|
|
|
|
// Write data to the UDS connection with write timeout and minimal error handling:
|
|
// create the connection if nil, and destroy it if the statsd server has disconnected
|
|
func (w *udsWriter) Write(data []byte) (int, error) {
|
|
conn, err := w.ensureConnection()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
conn.SetWriteDeadline(time.Now().Add(w.writeTimeout))
|
|
n, e := conn.Write(data)
|
|
|
|
if err, isNetworkErr := e.(net.Error); !isNetworkErr || !err.Temporary() {
|
|
// Statsd server disconnected, retry connecting at next packet
|
|
w.unsetConnection()
|
|
return 0, e
|
|
}
|
|
return n, e
|
|
}
|
|
|
|
func (w *udsWriter) Close() error {
|
|
if w.conn != nil {
|
|
return w.conn.Close()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *udsWriter) ensureConnection() (net.Conn, error) {
|
|
// Check if we've already got a socket we can use
|
|
w.RLock()
|
|
currentConn := w.conn
|
|
w.RUnlock()
|
|
|
|
if currentConn != nil {
|
|
return currentConn, nil
|
|
}
|
|
|
|
// Looks like we might need to connect - try again with write locking.
|
|
w.Lock()
|
|
defer w.Unlock()
|
|
if w.conn != nil {
|
|
return w.conn, nil
|
|
}
|
|
|
|
newConn, err := net.Dial(w.addr.Network(), w.addr.String())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
w.conn = newConn
|
|
return newConn, nil
|
|
}
|
|
|
|
func (w *udsWriter) unsetConnection() {
|
|
w.Lock()
|
|
defer w.Unlock()
|
|
w.conn = nil
|
|
}
|