influxdb/cluster/shard_writer.go

162 lines
3.1 KiB
Go
Raw Normal View History

2015-05-27 16:02:38 +00:00
package cluster
import (
"fmt"
"net"
2015-05-27 20:40:19 +00:00
"time"
2015-05-27 16:02:38 +00:00
"github.com/influxdb/influxdb/meta"
"github.com/influxdb/influxdb/tsdb"
"gopkg.in/fatih/pool.v2"
)
2015-05-27 16:02:38 +00:00
const (
writeShardRequestMessage byte = iota + 1
writeShardResponseMessage
)
// ShardWriter writes a set of points to a shard.
type ShardWriter struct {
pool *clientPool
timeout time.Duration
2015-05-26 20:51:11 +00:00
MetaStore interface {
Node(id uint64) (ni *meta.NodeInfo, err error)
2015-05-26 20:51:11 +00:00
}
}
2015-05-27 16:02:38 +00:00
// NewShardWriter returns a new instance of ShardWriter.
func NewShardWriter(timeout time.Duration) *ShardWriter {
return &ShardWriter{
pool: newClientPool(),
timeout: timeout,
2015-05-26 20:51:11 +00:00
}
}
func (w *ShardWriter) WriteShard(shardID, ownerID uint64, points []tsdb.Point) error {
c, err := w.dial(ownerID)
2015-05-26 20:51:11 +00:00
if err != nil {
return err
}
conn, ok := c.(*pool.PoolConn)
if !ok {
panic("wrong connection type")
}
defer func(conn net.Conn) {
conn.Close() // return to pool
}(conn)
2015-05-26 23:14:46 +00:00
// Build write request.
2015-05-27 16:02:38 +00:00
var request WriteShardRequest
request.SetShardID(shardID)
request.AddPoints(points)
// Marshal into protocol buffers.
2015-05-30 20:00:46 +00:00
buf, err := request.MarshalBinary()
if err != nil {
return err
}
2015-05-30 20:00:46 +00:00
// Write request.
2015-05-27 20:40:19 +00:00
conn.SetWriteDeadline(time.Now().Add(w.timeout))
2015-05-30 20:00:46 +00:00
if err := WriteTLV(conn, writeShardRequestMessage, buf); err != nil {
conn.MarkUnusable()
return err
}
2015-05-30 20:00:46 +00:00
// Read the response.
2015-05-27 20:40:19 +00:00
conn.SetReadDeadline(time.Now().Add(w.timeout))
2015-05-30 20:00:46 +00:00
_, buf, err = ReadTLV(conn)
if err != nil {
conn.MarkUnusable()
return err
}
2015-05-30 20:00:46 +00:00
// Unmarshal response.
2015-05-27 16:02:38 +00:00
var response WriteShardResponse
2015-05-30 20:00:46 +00:00
if err := response.UnmarshalBinary(buf); err != nil {
return err
}
if response.Code() != 0 {
return fmt.Errorf("error code %d: %s", response.Code(), response.Message())
}
return nil
}
2015-05-27 20:40:19 +00:00
func (c *ShardWriter) dial(nodeID uint64) (net.Conn, error) {
// If we don't have a connection pool for that addr yet, create one
_, ok := c.pool.getPool(nodeID)
if !ok {
factory := &connFactory{nodeID: nodeID, clientPool: c.pool, timeout: c.timeout}
factory.metaStore = c.MetaStore
p, err := pool.NewChannelPool(1, 3, factory.dial)
if err != nil {
return nil, err
}
c.pool.setPool(nodeID, p)
}
return c.pool.conn(nodeID)
}
func (w *ShardWriter) Close() error {
2015-05-27 16:02:38 +00:00
if w.pool == nil {
2015-05-26 20:51:11 +00:00
return fmt.Errorf("client already closed")
}
2015-05-27 16:06:04 +00:00
w.pool.close()
2015-05-27 16:02:38 +00:00
w.pool = nil
2015-05-26 20:51:11 +00:00
return nil
}
const (
maxConnections = 500
maxRetries = 3
)
var errMaxConnectionsExceeded = fmt.Errorf("can not exceed max connections of %d", maxConnections)
type connFactory struct {
nodeID uint64
timeout time.Duration
clientPool interface {
size() int
}
metaStore interface {
Node(id uint64) (ni *meta.NodeInfo, err error)
}
}
func (c *connFactory) dial() (net.Conn, error) {
if c.clientPool.size() > maxConnections {
return nil, errMaxConnectionsExceeded
}
2015-05-30 20:00:46 +00:00
ni, err := c.metaStore.Node(c.nodeID)
if err != nil {
return nil, err
}
if ni == nil {
return nil, fmt.Errorf("node %d does not exist", c.nodeID)
}
2015-05-30 20:00:46 +00:00
conn, err := net.DialTimeout("tcp", ni.Host, c.timeout)
if err != nil {
return nil, err
}
2015-06-05 22:54:12 +00:00
// Write a marker byte for cluster messages.
_, err = conn.Write([]byte{MuxHeader})
if err != nil {
conn.Close()
return nil, err
}
return conn, nil
}