182 lines
3.8 KiB
Go
182 lines
3.8 KiB
Go
package influxdb
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
|
|
"github.com/davecgh/go-spew/spew"
|
|
"github.com/influxdb/influxdb/influxql"
|
|
)
|
|
|
|
const (
|
|
MAX_MAP_RESPONSE_SIZE = 1024 * 1024 * 1024
|
|
)
|
|
|
|
// RemoteMapper implements the influxql.Mapper interface. The engine uses the remote mapper
|
|
// to pull map results from shards that only exist on other servers in the cluster.
|
|
type RemoteMapper struct {
|
|
dataNodes []*DataNode
|
|
resp *http.Response
|
|
results chan interface{}
|
|
unmarshal influxql.UnmarshalFunc
|
|
complete bool
|
|
|
|
Call string
|
|
Database string
|
|
MeasurementName string
|
|
TMin int64
|
|
TMax int64
|
|
SeriesIDs []uint32
|
|
ShardID uint64
|
|
Filters []string
|
|
WhereFields []*Field
|
|
SelectFields []*Field
|
|
SelectTags []string
|
|
Limit int
|
|
Interval int64
|
|
}
|
|
|
|
type MapResponse struct {
|
|
Err string `json:",omitempty"`
|
|
Data []byte
|
|
Completed bool `json:",omitempty"`
|
|
}
|
|
|
|
// Open is a no op, real work is done starting with Being
|
|
func (m *RemoteMapper) Open() error { return nil }
|
|
|
|
// Close the response body
|
|
func (m *RemoteMapper) Close() {
|
|
if m.resp != nil && m.resp.Body != nil {
|
|
m.resp.Body.Close()
|
|
}
|
|
}
|
|
|
|
// Begin sends a request to the remote server
|
|
func (m *RemoteMapper) Begin(c *influxql.Call, startingTime int64, limit int) error {
|
|
// get the function for unmarshaling results
|
|
f, err := influxql.InitializeUnmarshaller(c)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
m.unmarshal = f
|
|
|
|
if c != nil {
|
|
m.Call = c.String()
|
|
}
|
|
m.Limit = limit
|
|
|
|
// send the request to map to the remote server
|
|
b, err := json.Marshal(m)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := http.Post(m.dataNodes[0].URL.String()+"/run_mapper", "application/json", bytes.NewReader(b))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
m.resp = resp
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *RemoteMapper) NextInterval() (interface{}, error) {
|
|
if m.complete {
|
|
return nil, nil
|
|
}
|
|
|
|
chunk := make([]byte, MAX_MAP_RESPONSE_SIZE, MAX_MAP_RESPONSE_SIZE)
|
|
n, err := m.resp.Body.Read(chunk)
|
|
if err != nil {
|
|
warn("NextInterval err:", n, err.Error())
|
|
return nil, err
|
|
}
|
|
if n == 0 {
|
|
return nil, nil
|
|
}
|
|
warn("... ", string(chunk[:n]))
|
|
mr := &MapResponse{}
|
|
err = json.Unmarshal(chunk[:n], mr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if mr.Err != "" {
|
|
return nil, errors.New(mr.Err)
|
|
}
|
|
if mr.Completed {
|
|
m.complete = true
|
|
return nil, nil
|
|
}
|
|
v, err := m.unmarshal(mr.Data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
spew.Dump(v)
|
|
return v, nil
|
|
}
|
|
|
|
func (m *RemoteMapper) CallExpr() (*influxql.Call, error) {
|
|
if m.Call == "" {
|
|
return nil, nil
|
|
}
|
|
|
|
c, err := influxql.ParseExpr(m.Call)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
call, ok := c.(*influxql.Call)
|
|
|
|
if !ok {
|
|
return nil, errors.New("Could't marshal aggregate call")
|
|
}
|
|
return call, nil
|
|
}
|
|
|
|
func (m *RemoteMapper) FilterExprs() []influxql.Expr {
|
|
exprs := make([]influxql.Expr, len(m.SeriesIDs), len(m.SeriesIDs))
|
|
|
|
// if filters is empty, they're all nil. if filters has one element, all filters
|
|
// should be set to that. Otherwise marshal each filter
|
|
if len(m.Filters) == 1 {
|
|
f, _ := influxql.ParseExpr(m.Filters[0])
|
|
for i, _ := range exprs {
|
|
exprs[i] = f
|
|
}
|
|
} else if len(m.Filters) > 1 {
|
|
for i, s := range m.Filters {
|
|
f, _ := influxql.ParseExpr(s)
|
|
exprs[i] = f
|
|
}
|
|
}
|
|
|
|
return exprs
|
|
}
|
|
|
|
func (m *RemoteMapper) SetFilters(filters []influxql.Expr) {
|
|
l := filters[0]
|
|
allFiltersTheSame := true
|
|
for _, f := range filters {
|
|
if l != f {
|
|
allFiltersTheSame = false
|
|
break
|
|
}
|
|
}
|
|
|
|
// we don't need anything if they're all the same and nil
|
|
if l == nil && allFiltersTheSame {
|
|
return
|
|
} else if allFiltersTheSame { // just set one filter element since they're all the same
|
|
m.Filters = []string{l.String()}
|
|
return
|
|
}
|
|
|
|
// marshal all of them since there are different ones
|
|
m.Filters = make([]string, len(filters), len(filters))
|
|
for i, f := range filters {
|
|
m.Filters[i] = f.String()
|
|
}
|
|
}
|