github.com/rohankumardubey/aresdb@v0.0.2-0.20190517170215-e54e3ca06b9c/query/device_manager_test.go (about)

     1  //  Copyright (c) 2017-2018 Uber Technologies, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package query
    16  
    17  import (
    18  	"github.com/onsi/ginkgo"
    19  	. "github.com/onsi/gomega"
    20  	"github.com/uber/aresdb/memstore"
    21  	"github.com/uber/aresdb/query/common"
    22  	"github.com/uber/aresdb/query/expr"
    23  	"github.com/uber/aresdb/utils"
    24  	"sync"
    25  	"time"
    26  )
    27  
    28  var _ = ginkgo.Describe("device_manager", func() {
    29  	var deviceManager *DeviceManager
    30  	var leastMemStrategy deviceChooseStrategy
    31  	freeMemory := []int{400, 2000, 3000}
    32  	queryCounts := []int{1, 1, 0}
    33  	deviceCount := 3
    34  
    35  	ginkgo.BeforeEach(func() {
    36  		// create DeviceInfoMap
    37  		deviceInfoArray := make([]*DeviceInfo, deviceCount)
    38  		for device := 0; device < deviceCount; device++ {
    39  			deviceInfo := DeviceInfo{
    40  				DeviceID:             device,
    41  				QueryCount:           queryCounts[device],
    42  				TotalAvailableMemory: 3000,
    43  				FreeMemory:           freeMemory[device],
    44  				QueryMemoryUsageMap:  make(map[*AQLQuery]int, 0),
    45  			}
    46  			deviceInfoArray[device] = &deviceInfo
    47  		}
    48  
    49  		deviceManager = &DeviceManager{
    50  			RWMutex:            &sync.RWMutex{},
    51  			DeviceInfos:        deviceInfoArray,
    52  			Timeout:            5,
    53  			MaxAvailableMemory: 3000,
    54  		}
    55  
    56  		deviceManager.deviceAvailable = sync.NewCond(deviceManager)
    57  		leastMemStrategy = leastQueryCountAndMemoryStrategy{
    58  			deviceManager: deviceManager,
    59  		}
    60  	})
    61  
    62  	ginkgo.AfterEach(func() {
    63  		utils.ResetClockImplementation()
    64  	})
    65  
    66  	ginkgo.It("leastMemStrategy should work", func() {
    67  		deviceManager.strategy = leastMemStrategy
    68  		queries := [5]*AQLQuery{{}, {}, {}, {}}
    69  		devices := [5]int{}
    70  		// case 1: w/o device hint, return the device w/ least query count.
    71  		devices[0] = deviceManager.findDevice(queries[0], 1000, -1)
    72  		Ω(devices[0]).Should(Equal(2))
    73  		// 400 2000 2000
    74  		// 1 1 1
    75  
    76  		// case 2: w/ device hint, return the device if it meets the requirement
    77  		devices[1] = deviceManager.findDevice(queries[1], 1000, 2)
    78  		Ω(devices[1]).Should(Equal(2))
    79  		// 400 2000 1000
    80  		// 1 1 2
    81  
    82  		// case 3: w/ device hint, but cannot meet the requirements
    83  		devices[2] = deviceManager.findDevice(queries[2], 1500, 0)
    84  		Ω(devices[2]).Should(Equal(1))
    85  		// 400 500 1000
    86  		// 1 2 2
    87  
    88  		// case 4: unable to find a device that meets the requirement
    89  		devices[3] = deviceManager.findDevice(queries[3], 2000, 0)
    90  		Ω(devices[3]).Should(Equal(-1))
    91  		// 400 500 1000
    92  		// 1 2 2
    93  
    94  		// case 5: return device with least query count and least available memory.
    95  		devices[4] = deviceManager.findDevice(queries[4], 500, 0)
    96  		Ω(devices[4]).Should(Equal(1))
    97  		// 400 0 1000
    98  		// 1 3 2
    99  
   100  		Ω(deviceManager.DeviceInfos[0].FreeMemory).Should(BeEquivalentTo(400))
   101  		Ω(deviceManager.DeviceInfos[1].FreeMemory).Should(BeEquivalentTo(0))
   102  		Ω(deviceManager.DeviceInfos[2].FreeMemory).Should(BeEquivalentTo(1000))
   103  
   104  		Ω(deviceManager.DeviceInfos[0].QueryCount).Should(BeEquivalentTo(1))
   105  		Ω(deviceManager.DeviceInfos[1].QueryCount).Should(BeEquivalentTo(3))
   106  		Ω(deviceManager.DeviceInfos[2].QueryCount).Should(BeEquivalentTo(2))
   107  
   108  		for i := range devices {
   109  			deviceManager.ReleaseReservedMemory(devices[i], queries[i])
   110  		}
   111  
   112  		for device, deviceInfo := range deviceManager.DeviceInfos {
   113  			Ω(deviceInfo.FreeMemory).Should(Equal(freeMemory[device]))
   114  			Ω(deviceInfo.QueryCount).Should(Equal(queryCounts[device]))
   115  			Ω(deviceInfo.QueryMemoryUsageMap).Should(BeEmpty())
   116  		}
   117  	})
   118  
   119  	ginkgo.It("query queuing should work", func() {
   120  		deviceManager = &DeviceManager{
   121  			RWMutex: &sync.RWMutex{},
   122  			DeviceInfos: []*DeviceInfo{
   123  				{
   124  					DeviceID:             0,
   125  					QueryCount:           0,
   126  					TotalAvailableMemory: 1000,
   127  					FreeMemory:           500,
   128  					QueryMemoryUsageMap:  make(map[*AQLQuery]int, 0),
   129  				},
   130  			},
   131  			Timeout:            5,
   132  			MaxAvailableMemory: 1000,
   133  		}
   134  
   135  		leastQueryCountAndMemStrategy := leastQueryCountAndMemoryStrategy{
   136  			deviceManager: deviceManager,
   137  		}
   138  
   139  		deviceManager.strategy = leastQueryCountAndMemStrategy
   140  		deviceManager.deviceAvailable = sync.NewCond(deviceManager)
   141  
   142  		queries := [3]*AQLQuery{{}, {}, {}}
   143  		devices := [4]int{}
   144  		timeout := 3
   145  
   146  		utils.SetCurrentTime(time.Unix(0, 0))
   147  
   148  		// first assign 500 bytes to a query 1.
   149  		devices[0] = deviceManager.FindDevice(queries[0], 500, -1, timeout)
   150  		Ω(devices[0]).Should(Equal(0))
   151  		Ω(deviceManager.DeviceInfos[0].FreeMemory).Should(Equal(0))
   152  
   153  		wg := sync.WaitGroup{}
   154  		wg.Add(2)
   155  		// query 2:
   156  		// requires 500 bytes, needs to wait for release of query 1.
   157  		go func() {
   158  			defer wg.Done()
   159  			devices[1] = deviceManager.FindDevice(queries[1], 500, -1, timeout)
   160  			deviceManager.ReleaseReservedMemory(devices[1], queries[1])
   161  		}()
   162  
   163  		// query 3:
   164  		// requires 1000 bytes, will timeout.
   165  		go func() {
   166  			defer wg.Done()
   167  			devices[2] = deviceManager.FindDevice(queries[2], 1000, -1, timeout)
   168  			deviceManager.ReleaseReservedMemory(devices[2], queries[2])
   169  		}()
   170  
   171  		<-time.NewTimer(time.Second).C
   172  		// trigger broadcast, query 1 should finish now.
   173  		deviceManager.ReleaseReservedMemory(devices[0], queries[0])
   174  
   175  		<-time.NewTimer(time.Second).C
   176  		utils.SetCurrentTime(time.Unix(10, 0))
   177  		// trigger another broadcast, query 2 will timeout.
   178  		device := deviceManager.FindDevice(queries[0], 500, -1, timeout)
   179  		Ω(device).Should(Equal(0))
   180  		deviceManager.ReleaseReservedMemory(device, queries[0])
   181  
   182  		// wait for two go routines.
   183  		wg.Wait()
   184  		Ω(deviceManager.DeviceInfos[0].FreeMemory).Should(Equal(500))
   185  		Ω(devices[1]).Should(Equal(0))
   186  		Ω(devices[2]).Should(Equal(-1))
   187  		// query 4:
   188  		// requi0res 2000 bytes, exceeds max device memory.
   189  		device = deviceManager.FindDevice(queries[2], 2000, -1, timeout)
   190  		Ω(device).Should(Equal(-1))
   191  	})
   192  
   193  	ginkgo.It("estimate memory usage", func() {
   194  		testFactory := memstore.TestFactoryT{
   195  			RootPath:   "../testing/data",
   196  			FileSystem: utils.OSFileSystem{},
   197  		}
   198  		batch110, err := testFactory.ReadLiveBatch("archiving/batch-110")
   199  		Ω(err).Should(BeNil())
   200  		liveBatch := &memstore.LiveBatch{
   201  			Capacity: 5,
   202  			Batch: memstore.Batch{
   203  				RWMutex: &sync.RWMutex{},
   204  				Columns: batch110.Columns,
   205  			},
   206  		}
   207  
   208  		batchArchive0, err := testFactory.ReadArchiveBatch("archiving/archiveBatch0")
   209  		Ω(err).Should(BeNil())
   210  		archiveBatch := &memstore.ArchiveBatch{
   211  			Batch: memstore.Batch{
   212  				RWMutex: &sync.RWMutex{},
   213  				Columns: batchArchive0.Columns,
   214  			},
   215  			Size: 5,
   216  		}
   217  
   218  		qc := AQLQueryContext{
   219  			TableScanners: []*TableScanner{
   220  				{
   221  					Columns: []int{0, 2, 1},
   222  					ColumnUsages: map[int]columnUsage{
   223  						0: columnUsedByAllBatches,
   224  						1: columnUsedByAllBatches | columnUsedByPrefilter,
   225  						2: columnUsedByAllBatches,
   226  					},
   227  					EqualityPrefilterValues: []uint32{1},
   228  				},
   229  			},
   230  			OOPK: OOPKContext{
   231  				NumDimsPerDimWidth: common.DimCountsPerDimWidth{0, 0, 1, 0, 0},
   232  				DimRowBytes:        5,
   233  				MeasureBytes:       4,
   234  				foreignTables:      []*foreignTable{{}},
   235  				MainTableCommonFilters: []expr.Expr{
   236  					&expr.BinaryExpr{
   237  						Op: expr.OR,
   238  						LHS: &expr.BinaryExpr{
   239  							Op: expr.EQ,
   240  							LHS: &expr.UnaryExpr{
   241  								Op: expr.UNARY_MINUS,
   242  								Expr: &expr.VarRef{
   243  									Val:      "vp1",
   244  									ColumnID: 1,
   245  								},
   246  							},
   247  							RHS: &expr.NumberLiteral{
   248  								Val:      0,
   249  								Int:      -2,
   250  								ExprType: expr.Signed,
   251  							},
   252  						},
   253  						RHS: &expr.BinaryExpr{
   254  							Op: expr.GTE,
   255  							LHS: &expr.VarRef{
   256  								Val:      "vp2",
   257  								ColumnID: 2,
   258  							},
   259  							RHS: &expr.NumberLiteral{
   260  								Val:      0,
   261  								Int:      2,
   262  								ExprType: expr.Unsigned,
   263  							},
   264  						},
   265  					},
   266  				},
   267  				Dimensions: []expr.Expr{
   268  					&expr.BinaryExpr{
   269  						Op: expr.DIV,
   270  						LHS: &expr.VarRef{
   271  							Val:      "vp2",
   272  							ColumnID: 2,
   273  						},
   274  						RHS: &expr.NumberLiteral{
   275  							Val:      2,
   276  							Int:      2,
   277  							Expr:     "2",
   278  							ExprType: expr.Signed,
   279  						},
   280  						ExprType: expr.Signed,
   281  					},
   282  				},
   283  				Measure: &expr.BinaryExpr{
   284  					Op: expr.DIV,
   285  					LHS: &expr.VarRef{
   286  						Val:      "vp2",
   287  						ColumnID: 2,
   288  					},
   289  					RHS: &expr.NumberLiteral{
   290  						Val:      2,
   291  						Int:      2,
   292  						Expr:     "2",
   293  						ExprType: expr.Signed,
   294  					},
   295  				},
   296  			},
   297  		}
   298  
   299  		// expression
   300  		memUsage := qc.estimateExpressionEvaluationMemUsage(20)
   301  		Ω(memUsage).Should(Equal(20 * 5 * 2))
   302  
   303  		// sort reduce
   304  		memUsage = estimateSortReduceMemUsage(20)
   305  		Ω(memUsage).Should(Equal(720))
   306  
   307  		// batch processing
   308  		memUsage = qc.estimateMemUsageForBatch(20, 128+128+128)
   309  		Ω(memUsage).Should(Equal(1204))
   310  
   311  		// live batch columns + processing
   312  		memUsage = qc.estimateLiveBatchMemoryUsage(liveBatch)
   313  		Ω(memUsage).Should(Equal(589))
   314  
   315  		// archive batch columns + processing
   316  		memUsage = qc.estimateArchiveBatchMemoryUsage(archiveBatch, false)
   317  		Ω(memUsage).Should(Equal(489))
   318  	})
   319  })