influxdb/cluster/shard_mapper.go

260 lines
6.1 KiB
Go
Raw Normal View History

2015-07-16 00:52:24 +00:00
package cluster
import (
2015-09-16 20:17:58 +00:00
"encoding/json"
"fmt"
"math/rand"
"net"
"time"
2015-07-16 03:06:07 +00:00
"github.com/influxdb/influxdb/influxql"
"github.com/influxdb/influxdb/meta"
2015-07-16 00:52:24 +00:00
"github.com/influxdb/influxdb/tsdb"
2015-07-16 03:06:07 +00:00
)
2015-07-16 00:52:24 +00:00
// ShardMapper is responsible for providing mappers for requested shards. It is
// responsible for creating those mappers from the local store, or reaching
// out to another node on the cluster.
type ShardMapper struct {
ForceRemoteMapping bool // All shards treated as remote. Useful for testing.
2015-07-16 00:52:24 +00:00
MetaStore interface {
NodeID() uint64
Node(id uint64) (ni *meta.NodeInfo, err error)
2015-07-16 00:52:24 +00:00
}
TSDBStore interface {
CreateMapper(shardID uint64, stmt influxql.Statement, chunkSize int) (tsdb.Mapper, error)
2015-07-16 00:52:24 +00:00
}
timeout time.Duration
pool *clientPool
2015-07-16 00:52:24 +00:00
}
2015-07-16 03:06:07 +00:00
// NewShardMapper returns a mapper of local and remote shards.
func NewShardMapper(timeout time.Duration) *ShardMapper {
return &ShardMapper{
pool: newClientPool(),
timeout: timeout,
}
2015-07-16 00:52:24 +00:00
}
2015-07-16 03:06:07 +00:00
// CreateMapper returns a Mapper for the given shard ID.
func (s *ShardMapper) CreateMapper(sh meta.ShardInfo, stmt influxql.Statement, chunkSize int) (tsdb.Mapper, error) {
2015-09-16 20:17:58 +00:00
// Create a remote mapper if the local node doesn't own the shard.
2015-08-19 17:12:56 +00:00
if !sh.OwnedBy(s.MetaStore.NodeID()) || s.ForceRemoteMapping {
// Pick a node in a pseudo-random manner.
conn, err := s.dial(sh.Owners[rand.Intn(len(sh.Owners))].NodeID)
if err != nil {
return nil, err
}
conn.SetDeadline(time.Now().Add(s.timeout))
2015-09-16 20:17:58 +00:00
return NewRemoteMapper(conn, sh.ID, stmt, chunkSize), nil
}
// If it is local then return the mapper from the store.
m, err := s.TSDBStore.CreateMapper(sh.ID, stmt, chunkSize)
if err != nil {
return nil, err
2015-07-16 00:52:24 +00:00
}
2015-07-16 00:52:24 +00:00
return m, nil
}
2015-07-16 03:06:07 +00:00
func (s *ShardMapper) dial(nodeID uint64) (net.Conn, error) {
ni, err := s.MetaStore.Node(nodeID)
if err != nil {
return nil, err
}
conn, err := net.Dial("tcp", ni.Host)
if err != nil {
return nil, err
}
// Write the cluster multiplexing header byte
conn.Write([]byte{MuxHeader})
return conn, nil
}
2015-07-16 03:06:07 +00:00
// RemoteMapper implements the tsdb.Mapper interface. It connects to a remote node,
// sends a query, and interprets the stream of data that comes back.
type RemoteMapper struct {
shardID uint64
stmt influxql.Statement
2015-07-16 03:06:07 +00:00
chunkSize int
tagsets []string
fields []string
2015-07-16 03:06:07 +00:00
conn net.Conn
bufferedResponse *MapShardResponse
2015-09-16 20:17:58 +00:00
unmarshallers []tsdb.UnmarshalFunc // Mapping-specific unmarshal functions.
2015-07-16 03:06:07 +00:00
}
// NewRemoteMapper returns a new remote mapper using the given connection.
func NewRemoteMapper(c net.Conn, shardID uint64, stmt influxql.Statement, chunkSize int) *RemoteMapper {
2015-07-16 03:06:07 +00:00
return &RemoteMapper{
conn: c,
2015-07-16 03:06:07 +00:00
shardID: shardID,
stmt: stmt,
chunkSize: chunkSize,
}
}
// Open connects to the remote node and starts receiving data.
func (r *RemoteMapper) Open() (err error) {
defer func() {
if err != nil {
r.conn.Close()
}
}()
2015-09-16 20:17:58 +00:00
// Build Map request.
var request MapShardRequest
request.SetShardID(r.shardID)
request.SetQuery(r.stmt.String())
request.SetChunkSize(int32(r.chunkSize))
// Marshal into protocol buffers.
buf, err := request.MarshalBinary()
2015-07-16 03:06:07 +00:00
if err != nil {
return err
}
// Write request.
if err := WriteTLV(r.conn, mapShardRequestMessage, buf); err != nil {
return err
2015-07-16 03:06:07 +00:00
}
// Read the response.
_, buf, err = ReadTLV(r.conn)
if err != nil {
return err
2015-07-16 03:06:07 +00:00
}
// Unmarshal response.
r.bufferedResponse = &MapShardResponse{}
if err := r.bufferedResponse.UnmarshalBinary(buf); err != nil {
2015-07-16 03:06:07 +00:00
return err
}
if r.bufferedResponse.Code() != 0 {
return fmt.Errorf("error code %d: %s", r.bufferedResponse.Code(), r.bufferedResponse.Message())
}
2015-07-16 03:06:07 +00:00
// Decode the first response to get the TagSets.
r.tagsets = r.bufferedResponse.TagSets()
2015-08-19 17:12:56 +00:00
r.fields = r.bufferedResponse.Fields()
2015-07-16 03:06:07 +00:00
2015-09-16 20:17:58 +00:00
// Set up each mapping function for this statement.
if stmt, ok := r.stmt.(*influxql.SelectStatement); ok {
for _, c := range stmt.FunctionCalls() {
fn, err := tsdb.InitializeUnmarshaller(c)
if err != nil {
return err
}
r.unmarshallers = append(r.unmarshallers, fn)
}
}
2015-07-16 03:06:07 +00:00
2015-09-16 20:17:58 +00:00
return nil
2015-08-19 17:12:56 +00:00
}
// TagSets returns the TagSets
2015-07-16 03:06:07 +00:00
func (r *RemoteMapper) TagSets() []string {
return r.tagsets
}
// Fields returns RemoteMapper's Fields
func (r *RemoteMapper) Fields() []string {
return r.fields
}
2015-07-16 03:06:07 +00:00
// NextChunk returns the next chunk read from the remote node to the client.
func (r *RemoteMapper) NextChunk() (chunk interface{}, err error) {
var response *MapShardResponse
if r.bufferedResponse != nil {
response = r.bufferedResponse
r.bufferedResponse = nil
} else {
response = &MapShardResponse{}
// Read the response.
_, buf, err := ReadTLV(r.conn)
if err != nil {
return nil, err
}
// Unmarshal response.
if err := response.UnmarshalBinary(buf); err != nil {
return nil, err
}
if response.Code() != 0 {
return nil, fmt.Errorf("error code %d: %s", response.Code(), response.Message())
}
2015-07-16 03:06:07 +00:00
}
if response.Data() == nil {
return nil, nil
}
2015-08-19 17:12:56 +00:00
2015-09-16 20:17:58 +00:00
moj := &tsdb.MapperOutputJSON{}
if err := json.Unmarshal(response.Data(), moj); err != nil {
return nil, err
}
mvj := []*tsdb.MapperValueJSON{}
if err := json.Unmarshal(moj.Values, &mvj); err != nil {
return nil, err
}
// Prep the non-JSON version of Mapper output.
mo := &tsdb.MapperOutput{
Name: moj.Name,
Tags: moj.Tags,
Fields: moj.Fields,
}
if len(mvj) == 1 && len(mvj[0].AggData) > 0 {
// The MapperValue is carrying aggregate data, so run it through the
// custom unmarshallers for the map functions through which the data
// was mapped.
aggValues := []interface{}{}
for i, b := range mvj[0].AggData {
v, err := r.unmarshallers[i](b)
if err != nil {
return nil, err
}
aggValues = append(aggValues, v)
}
mo.Values = []*tsdb.MapperValue{&tsdb.MapperValue{
Value: aggValues,
Tags: mvj[0].Tags,
}}
} else {
// Must be raw data instead.
for _, v := range mvj {
var rawValue interface{}
if err := json.Unmarshal(v.RawData, &rawValue); err != nil {
return nil, err
}
mo.Values = append(mo.Values, &tsdb.MapperValue{
Time: v.Time,
Value: rawValue,
Tags: v.Tags,
})
}
}
return mo, nil
2015-07-16 03:06:07 +00:00
}
// Close the Mapper
2015-07-16 03:06:07 +00:00
func (r *RemoteMapper) Close() {
r.conn.Close()
2015-07-16 03:06:07 +00:00
}