2016-05-11 16:32:56 +00:00
package coordinator_test
2016-02-19 20:38:02 +00:00
import (
"bytes"
2016-03-23 15:05:38 +00:00
"errors"
2016-02-19 20:38:02 +00:00
"io"
2016-04-20 20:07:08 +00:00
"log"
2016-02-19 20:38:02 +00:00
"os"
"reflect"
"testing"
"time"
"github.com/davecgh/go-spew/spew"
2016-05-11 16:32:56 +00:00
"github.com/influxdata/influxdb/coordinator"
2016-02-19 20:38:02 +00:00
"github.com/influxdata/influxdb/influxql"
2016-10-27 22:17:08 +00:00
"github.com/influxdata/influxdb/internal"
2016-02-19 20:38:02 +00:00
"github.com/influxdata/influxdb/models"
"github.com/influxdata/influxdb/services/meta"
2016-06-06 19:53:54 +00:00
"github.com/influxdata/influxdb/tsdb"
2016-02-19 20:38:02 +00:00
)
const (
// DefaultDatabase is the default database name used in tests.
DefaultDatabase = "db0"
// DefaultRetentionPolicy is the default retention policy name used in tests.
DefaultRetentionPolicy = "rp0"
)
// Ensure query executor can execute a simple SELECT statement.
func TestQueryExecutor_ExecuteQuery_SelectStatement ( t * testing . T ) {
e := DefaultQueryExecutor ( )
// The meta client should return a single shard owned by the local node.
e . MetaClient . ShardsByTimeRangeFn = func ( sources influxql . Sources , tmin , tmax time . Time ) ( a [ ] meta . ShardInfo , err error ) {
return [ ] meta . ShardInfo { { ID : 100 , Owners : [ ] meta . ShardOwner { { NodeID : 0 } } } } , nil
}
// The TSDB store should return an IteratorCreator for shard.
// This IteratorCreator returns a single iterator with "value" in the aux fields.
e . TSDBStore . ShardIteratorCreatorFn = func ( id uint64 ) influxql . IteratorCreator {
if id != 100 {
t . Fatalf ( "unexpected shard id: %d" , id )
}
var ic IteratorCreator
ic . CreateIteratorFn = func ( opt influxql . IteratorOptions ) ( influxql . Iterator , error ) {
return & FloatIterator { Points : [ ] influxql . FloatPoint {
{ Name : "cpu" , Time : int64 ( 0 * time . Second ) , Aux : [ ] interface { } { float64 ( 100 ) } } ,
{ Name : "cpu" , Time : int64 ( 1 * time . Second ) , Aux : [ ] interface { } { float64 ( 200 ) } } ,
} } , nil
}
2016-05-16 16:08:28 +00:00
ic . FieldDimensionsFn = func ( sources influxql . Sources ) ( fields map [ string ] influxql . DataType , dimensions map [ string ] struct { } , err error ) {
return map [ string ] influxql . DataType { "value" : influxql . Float } , nil , nil
2016-02-19 20:38:02 +00:00
}
return & ic
}
// Verify all results from the query.
if a := ReadAllResults ( e . ExecuteQuery ( ` SELECT * FROM cpu ` , "db0" , 0 ) ) ; ! reflect . DeepEqual ( a , [ ] * influxql . Result {
{
StatementID : 0 ,
Series : [ ] * models . Row { {
Name : "cpu" ,
Columns : [ ] string { "time" , "value" } ,
Values : [ ] [ ] interface { } {
{ time . Unix ( 0 , 0 ) . UTC ( ) , float64 ( 100 ) } ,
{ time . Unix ( 1 , 0 ) . UTC ( ) , float64 ( 200 ) } ,
} ,
} } ,
} ,
} ) {
t . Fatalf ( "unexpected results: %s" , spew . Sdump ( a ) )
}
}
2016-03-31 01:00:29 +00:00
// Ensure query executor can enforce a maximum bucket selection count.
func TestQueryExecutor_ExecuteQuery_MaxSelectBucketsN ( t * testing . T ) {
e := DefaultQueryExecutor ( )
2016-03-31 22:12:29 +00:00
e . StatementExecutor . MaxSelectBucketsN = 3
2016-03-31 01:00:29 +00:00
// The meta client should return a single shards on the local node.
e . MetaClient . ShardsByTimeRangeFn = func ( sources influxql . Sources , tmin , tmax time . Time ) ( a [ ] meta . ShardInfo , err error ) {
return [ ] meta . ShardInfo {
{ ID : 100 , Owners : [ ] meta . ShardOwner { { NodeID : 0 } } } ,
} , nil
}
var ic IteratorCreator
ic . CreateIteratorFn = func ( opt influxql . IteratorOptions ) ( influxql . Iterator , error ) {
return & FloatIterator {
Points : [ ] influxql . FloatPoint { { Name : "cpu" , Time : int64 ( 0 * time . Second ) , Aux : [ ] interface { } { float64 ( 100 ) } } } ,
} , nil
}
2016-05-16 16:08:28 +00:00
ic . FieldDimensionsFn = func ( sources influxql . Sources ) ( fields map [ string ] influxql . DataType , dimensions map [ string ] struct { } , err error ) {
return map [ string ] influxql . DataType { "value" : influxql . Float } , nil , nil
2016-03-31 01:00:29 +00:00
}
e . TSDBStore . ShardIteratorCreatorFn = func ( id uint64 ) influxql . IteratorCreator { return & ic }
// Verify all results from the query.
if a := ReadAllResults ( e . ExecuteQuery ( ` SELECT count(value) FROM cpu WHERE time >= '2000-01-01T00:00:05Z' AND time < '2000-01-01T00:00:35Z' GROUP BY time(10s) ` , "db0" , 0 ) ) ; ! reflect . DeepEqual ( a , [ ] * influxql . Result {
{
StatementID : 0 ,
2016-10-28 20:44:33 +00:00
Err : errors . New ( "max-select-buckets limit exceeded: (4/3)" ) ,
2016-03-31 01:00:29 +00:00
} ,
} ) {
t . Fatalf ( "unexpected results: %s" , spew . Sdump ( a ) )
2016-03-23 15:05:38 +00:00
}
}
2016-10-27 22:17:08 +00:00
func TestStatementExecutor_NormalizeDropSeries ( t * testing . T ) {
q , err := influxql . ParseQuery ( "DROP SERIES FROM cpu" )
if err != nil {
t . Fatalf ( "unexpected error parsing query: %v" , err )
}
stmt := q . Statements [ 0 ] . ( * influxql . DropSeriesStatement )
s := & coordinator . StatementExecutor {
MetaClient : & internal . MetaClientMock {
DatabaseFn : func ( name string ) * meta . DatabaseInfo {
t . Fatal ( "meta client should not be called" )
return nil
} ,
} ,
}
if err := s . NormalizeStatement ( stmt , "foo" ) ; err != nil {
t . Fatalf ( "unexpected error normalizing statement: %v" , err )
}
m := stmt . Sources [ 0 ] . ( * influxql . Measurement )
if m . Database != "" {
t . Fatalf ( "database rewritten when not supposed to: %v" , m . Database )
}
if m . RetentionPolicy != "" {
t . Fatalf ( "database rewritten when not supposed to: %v" , m . RetentionPolicy )
}
if exp , got := "DROP SERIES FROM cpu" , q . String ( ) ; exp != got {
t . Fatalf ( "generated query does match parsed: exp %v, got %v" , exp , got )
}
}
func TestStatementExecutor_NormalizeDeleteSeries ( t * testing . T ) {
q , err := influxql . ParseQuery ( "DELETE FROM cpu" )
if err != nil {
t . Fatalf ( "unexpected error parsing query: %v" , err )
}
stmt := q . Statements [ 0 ] . ( * influxql . DeleteSeriesStatement )
s := & coordinator . StatementExecutor {
MetaClient : & internal . MetaClientMock {
DatabaseFn : func ( name string ) * meta . DatabaseInfo {
t . Fatal ( "meta client should not be called" )
return nil
} ,
} ,
}
if err := s . NormalizeStatement ( stmt , "foo" ) ; err != nil {
t . Fatalf ( "unexpected error normalizing statement: %v" , err )
}
m := stmt . Sources [ 0 ] . ( * influxql . Measurement )
if m . Database != "" {
t . Fatalf ( "database rewritten when not supposed to: %v" , m . Database )
}
if m . RetentionPolicy != "" {
t . Fatalf ( "database rewritten when not supposed to: %v" , m . RetentionPolicy )
}
if exp , got := "DELETE FROM cpu" , q . String ( ) ; exp != got {
t . Fatalf ( "generated query does match parsed: exp %v, got %v" , exp , got )
}
}
2016-05-11 16:32:56 +00:00
// QueryExecutor is a test wrapper for coordinator.QueryExecutor.
2016-02-19 20:38:02 +00:00
type QueryExecutor struct {
2016-03-31 22:12:29 +00:00
* influxql . QueryExecutor
2016-02-19 20:38:02 +00:00
2016-03-31 22:12:29 +00:00
MetaClient MetaClient
TSDBStore TSDBStore
2016-05-11 16:32:56 +00:00
StatementExecutor * coordinator . StatementExecutor
2016-03-31 22:12:29 +00:00
LogOutput bytes . Buffer
2016-02-19 20:38:02 +00:00
}
// NewQueryExecutor returns a new instance of QueryExecutor.
// This query executor always has a node id of 0.
func NewQueryExecutor ( ) * QueryExecutor {
e := & QueryExecutor {
2016-03-31 22:12:29 +00:00
QueryExecutor : influxql . NewQueryExecutor ( ) ,
2016-02-19 20:38:02 +00:00
}
2016-05-11 16:32:56 +00:00
e . StatementExecutor = & coordinator . StatementExecutor {
2016-03-31 22:12:29 +00:00
MetaClient : & e . MetaClient ,
TSDBStore : & e . TSDBStore ,
}
e . QueryExecutor . StatementExecutor = e . StatementExecutor
2016-02-19 20:38:02 +00:00
2016-04-20 20:07:08 +00:00
var out io . Writer = & e . LogOutput
2016-02-19 20:38:02 +00:00
if testing . Verbose ( ) {
2016-04-20 20:07:08 +00:00
out = io . MultiWriter ( out , os . Stderr )
2016-02-19 20:38:02 +00:00
}
2016-04-20 20:07:08 +00:00
e . QueryExecutor . Logger = log . New ( out , "[query] " , log . LstdFlags )
2016-02-19 20:38:02 +00:00
return e
}
// DefaultQueryExecutor returns a QueryExecutor with a database (db0) and retention policy (rp0).
func DefaultQueryExecutor ( ) * QueryExecutor {
e := NewQueryExecutor ( )
e . MetaClient . DatabaseFn = DefaultMetaClientDatabaseFn
return e
}
// ExecuteQuery parses query and executes against the database.
func ( e * QueryExecutor ) ExecuteQuery ( query , database string , chunkSize int ) <- chan * influxql . Result {
2016-06-01 17:30:50 +00:00
return e . QueryExecutor . ExecuteQuery ( MustParseQuery ( query ) , influxql . ExecutionOptions {
Database : database ,
ChunkSize : chunkSize ,
} , make ( chan struct { } ) )
2016-02-19 20:38:02 +00:00
}
2016-05-11 16:32:56 +00:00
// TSDBStore is a mockable implementation of coordinator.TSDBStore.
2016-02-19 20:38:02 +00:00
type TSDBStore struct {
2016-06-01 22:17:18 +00:00
CreateShardFn func ( database , policy string , shardID uint64 , enabled bool ) error
2016-02-19 20:38:02 +00:00
WriteToShardFn func ( shardID uint64 , points [ ] models . Point ) error
2016-04-29 00:29:09 +00:00
RestoreShardFn func ( id uint64 , r io . Reader ) error
BackupShardFn func ( id uint64 , since time . Time , w io . Writer ) error
2016-03-31 22:12:29 +00:00
DeleteDatabaseFn func ( name string ) error
DeleteMeasurementFn func ( database , name string ) error
DeleteRetentionPolicyFn func ( database , name string ) error
DeleteShardFn func ( id uint64 ) error
2016-04-29 22:31:57 +00:00
DeleteSeriesFn func ( database string , sources [ ] influxql . Source , condition influxql . Expr ) error
2016-06-06 19:53:54 +00:00
DatabaseIndexFn func ( name string ) * tsdb . DatabaseIndex
2016-03-31 22:12:29 +00:00
ShardIteratorCreatorFn func ( id uint64 ) influxql . IteratorCreator
2016-02-19 20:38:02 +00:00
}
2016-06-01 22:17:18 +00:00
func ( s * TSDBStore ) CreateShard ( database , policy string , shardID uint64 , enabled bool ) error {
2016-03-01 23:44:07 +00:00
if s . CreateShardFn == nil {
return nil
}
2016-06-01 22:17:18 +00:00
return s . CreateShardFn ( database , policy , shardID , enabled )
2016-02-19 20:38:02 +00:00
}
func ( s * TSDBStore ) WriteToShard ( shardID uint64 , points [ ] models . Point ) error {
return s . WriteToShardFn ( shardID , points )
}
2016-04-29 00:29:09 +00:00
func ( s * TSDBStore ) RestoreShard ( id uint64 , r io . Reader ) error {
return s . RestoreShardFn ( id , r )
}
func ( s * TSDBStore ) BackupShard ( id uint64 , since time . Time , w io . Writer ) error {
return s . BackupShardFn ( id , since , w )
}
2016-02-19 20:38:02 +00:00
func ( s * TSDBStore ) DeleteDatabase ( name string ) error {
return s . DeleteDatabaseFn ( name )
}
func ( s * TSDBStore ) DeleteMeasurement ( database , name string ) error {
return s . DeleteMeasurementFn ( database , name )
}
func ( s * TSDBStore ) DeleteRetentionPolicy ( database , name string ) error {
return s . DeleteRetentionPolicyFn ( database , name )
}
2016-03-11 15:53:15 +00:00
func ( s * TSDBStore ) DeleteShard ( id uint64 ) error {
return s . DeleteShardFn ( id )
}
2016-04-29 22:31:57 +00:00
func ( s * TSDBStore ) DeleteSeries ( database string , sources [ ] influxql . Source , condition influxql . Expr ) error {
return s . DeleteSeriesFn ( database , sources , condition )
2016-02-19 20:38:02 +00:00
}
2016-06-10 15:14:21 +00:00
func ( s * TSDBStore ) IteratorCreator ( shards [ ] meta . ShardInfo , opt * influxql . SelectOptions ) ( influxql . IteratorCreator , error ) {
2016-03-31 22:12:29 +00:00
// Generate iterators for each node.
ics := make ( [ ] influxql . IteratorCreator , 0 )
if err := func ( ) error {
2016-04-05 19:33:44 +00:00
for _ , shard := range shards {
ic := s . ShardIteratorCreator ( shard . ID )
2016-03-31 22:12:29 +00:00
if ic == nil {
continue
}
ics = append ( ics , ic )
}
2016-02-19 20:38:02 +00:00
2016-03-31 22:12:29 +00:00
return nil
} ( ) ; err != nil {
influxql . IteratorCreators ( ics ) . Close ( )
return nil , err
}
2016-02-19 20:38:02 +00:00
2016-03-31 22:12:29 +00:00
return influxql . IteratorCreators ( ics ) , nil
2016-02-19 20:38:02 +00:00
}
func ( s * TSDBStore ) ShardIteratorCreator ( id uint64 ) influxql . IteratorCreator {
return s . ShardIteratorCreatorFn ( id )
}
2016-06-06 19:53:54 +00:00
func ( s * TSDBStore ) DatabaseIndex ( name string ) * tsdb . DatabaseIndex {
return s . DatabaseIndexFn ( name )
}
2016-07-28 22:38:08 +00:00
func ( s * TSDBStore ) Measurements ( database string , cond influxql . Expr ) ( [ ] string , error ) {
return nil , nil
}
func ( s * TSDBStore ) TagValues ( database string , cond influxql . Expr ) ( [ ] tsdb . TagValues , error ) {
return nil , nil
}
2016-02-19 20:38:02 +00:00
// MustParseQuery parses s into a query. Panic on error.
func MustParseQuery ( s string ) * influxql . Query {
q , err := influxql . ParseQuery ( s )
if err != nil {
panic ( err )
}
return q
}
// ReadAllResults reads all results from c and returns as a slice.
func ReadAllResults ( c <- chan * influxql . Result ) [ ] * influxql . Result {
var a [ ] * influxql . Result
for result := range c {
a = append ( a , result )
}
return a
}
// IteratorCreator is a mockable implementation of IteratorCreator.
type IteratorCreator struct {
CreateIteratorFn func ( opt influxql . IteratorOptions ) ( influxql . Iterator , error )
2016-05-16 16:08:28 +00:00
FieldDimensionsFn func ( sources influxql . Sources ) ( fields map [ string ] influxql . DataType , dimensions map [ string ] struct { } , err error )
2016-03-04 18:01:41 +00:00
ExpandSourcesFn func ( sources influxql . Sources ) ( influxql . Sources , error )
2016-02-19 20:38:02 +00:00
}
func ( ic * IteratorCreator ) CreateIterator ( opt influxql . IteratorOptions ) ( influxql . Iterator , error ) {
return ic . CreateIteratorFn ( opt )
}
2016-05-16 16:08:28 +00:00
func ( ic * IteratorCreator ) FieldDimensions ( sources influxql . Sources ) ( fields map [ string ] influxql . DataType , dimensions map [ string ] struct { } , err error ) {
2016-02-19 20:38:02 +00:00
return ic . FieldDimensionsFn ( sources )
}
2016-03-04 18:01:41 +00:00
func ( ic * IteratorCreator ) ExpandSources ( sources influxql . Sources ) ( influxql . Sources , error ) {
return ic . ExpandSourcesFn ( sources )
}
2016-02-19 20:38:02 +00:00
// FloatIterator is a represents an iterator that reads from a slice.
type FloatIterator struct {
Points [ ] influxql . FloatPoint
2016-03-23 15:05:38 +00:00
stats influxql . IteratorStats
2016-02-19 20:38:02 +00:00
}
2016-03-23 15:05:38 +00:00
func ( itr * FloatIterator ) Stats ( ) influxql . IteratorStats { return itr . stats }
2016-03-17 15:55:37 +00:00
func ( itr * FloatIterator ) Close ( ) error { return nil }
2016-02-19 20:38:02 +00:00
// Next returns the next value and shifts it off the beginning of the points slice.
2016-04-17 20:00:59 +00:00
func ( itr * FloatIterator ) Next ( ) ( * influxql . FloatPoint , error ) {
2016-02-19 20:38:02 +00:00
if len ( itr . Points ) == 0 {
2016-04-17 20:00:59 +00:00
return nil , nil
2016-02-19 20:38:02 +00:00
}
v := & itr . Points [ 0 ]
itr . Points = itr . Points [ 1 : ]
2016-04-17 20:00:59 +00:00
return v , nil
2016-02-19 20:38:02 +00:00
}