github.com/rohankumardubey/aresdb@v0.0.2-0.20190517170215-e54e3ca06b9c/api/debug_handler_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 api 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "io/ioutil" 21 "net/http" 22 "net/http/httptest" 23 24 "time" 25 26 "github.com/gorilla/mux" 27 "github.com/onsi/ginkgo" 28 . "github.com/onsi/gomega" 29 "github.com/uber/aresdb/diskstore" 30 "github.com/uber/aresdb/memstore" 31 memCom "github.com/uber/aresdb/memstore/common" 32 memMocks "github.com/uber/aresdb/memstore/mocks" 33 "github.com/uber/aresdb/metastore" 34 metaCom "github.com/uber/aresdb/metastore/common" 35 "github.com/uber/aresdb/utils" 36 utilsMocks "github.com/uber/aresdb/utils/mocks" 37 38 "path/filepath" 39 40 "strconv" 41 42 "bytes" 43 "github.com/pkg/errors" 44 "github.com/stretchr/testify/mock" 45 "github.com/uber/aresdb/common" 46 "github.com/uber/aresdb/query" 47 "sync" 48 "unsafe" 49 ) 50 51 // convertToAPIError wraps up an error into APIError 52 func convertToAPIError(err error) error { 53 if _, ok := err.(utils.APIError); ok { 54 return err 55 } 56 57 return utils.APIError{ 58 Message: err.Error(), 59 } 60 } 61 62 var _ = ginkgo.Describe("DebugHandler", func() { 63 64 testFactory := memstore.TestFactoryT{ 65 RootPath: "../testing/data", 66 FileSystem: utils.OSFileSystem{}, 67 } 68 69 testTableName := "test" 70 testTableShardID := 1 71 var batchID int32 = 1 72 testTable := metaCom.Table{ 73 Name: testTableName, 74 IsFactTable: true, 75 Columns: []metaCom.Column{ 76 { 77 Name: "c0", 78 }, 79 { 80 Name: "c1", 81 }, 82 { 83 Name: "c2", 84 }, 85 { 86 Name: "c3", 87 }, 88 { 89 Name: "c4", 90 }, 91 { 92 Name: "c5", 93 Type: metaCom.Bool, 94 }, 95 { 96 Name: "c6", 97 Type: metaCom.SmallEnum, 98 }, 99 }, 100 Config: metaCom.TableConfig{ 101 BatchSize: 10, 102 BackfillMaxBufferSize: 1 << 32, 103 BackfillThresholdInBytes: 1 << 21, 104 }, 105 } 106 var testSchema *memstore.TableSchema 107 108 redoLogTableName := "abc" 109 redoLogShardID := 0 110 var redoLogFile int64 = 1501869573 111 112 var memStore *memMocks.MemStore 113 var testServer *httptest.Server 114 var debugHandler *DebugHandler 115 var scheduler *memMocks.Scheduler 116 117 ginkgo.BeforeEach(func() { 118 testBatch, _ := testFactory.ReadArchiveBatch("archiveBatch") 119 testArchiveBatch := memstore.ArchiveBatch{ 120 Batch: memstore.Batch{ 121 RWMutex: &sync.RWMutex{}, 122 Columns: testBatch.Columns, 123 }, 124 Size: 5, 125 Version: 0, 126 } 127 mockMetaStore := CreateMockMetaStore() 128 mockDiskStore := CreateMockDiskStore() 129 testRootPath := "../testing/data/integration/sample-ares-root" 130 testDiskStore := diskstore.NewLocalDiskStore(testRootPath) 131 testMetaStore, err := metastore.NewDiskMetaStore(filepath.Join(testRootPath, "metastore")) 132 Ω(err).Should(BeNil()) 133 134 // test table 135 testSchema = memstore.NewTableSchema(&testTable) 136 for col := range testSchema.Schema.Columns { 137 testSchema.SetDefaultValue(col) 138 } 139 140 testSchema.EnumDicts["c6"] = memstore.EnumDict{} 141 enumDict, ok := testSchema.EnumDicts["c6"] 142 Ω(ok).Should(BeTrue()) 143 enumDict.ReverseDict = append(enumDict.ReverseDict, "enum case1") 144 testSchema.EnumDicts["c6"] = enumDict 145 memStore = CreateMemStore(testSchema, testTableShardID, mockMetaStore, mockDiskStore) 146 testShard, _ := memStore.GetTableShard(testTableName, testTableShardID) 147 memstore.NewArchiveStoreVersion(100, testShard) 148 testShard.Schema = testSchema 149 testShard.ArchiveStore = &memstore.ArchiveStore{ 150 CurrentVersion: memstore.NewArchiveStoreVersion(100, testShard), 151 } 152 153 testShard.ArchiveStore.CurrentVersion.Batches = map[int32]*memstore.ArchiveBatch{ 154 batchID: &testArchiveBatch, 155 } 156 157 testShard.LiveStore = memstore.NewLiveStore( 158 10, 159 testShard, 160 ) 161 162 testArchiveBatch.Shard = testShard 163 164 key := []byte{1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1} 165 recordID := memstore.RecordID{ 166 BatchID: 1, 167 Index: 1, 168 } 169 170 testShard.LiveStore.PrimaryKey.FindOrInsert(key, recordID, 1) 171 172 // Create first batch. 173 testShard.LiveStore.AdvanceNextWriteRecord() 174 testShard.LiveStore.AdvanceLastReadRecord() 175 liveBatch := testShard.LiveStore.GetBatchForWrite(memstore.BaseBatchID) 176 liveBatch.Unlock() 177 vp := liveBatch.GetOrCreateVectorParty(5, false) 178 vp.SetDataValue(0, memCom.DataValue{Valid: true}, memstore.IgnoreCount) 179 180 var val uint8 = 0 181 vp = liveBatch.GetOrCreateVectorParty(6, false) 182 vp.SetDataValue(0, memCom.DataValue{Valid: true, OtherVal: unsafe.Pointer(&val)}, memstore.IgnoreCount) 183 184 // redolog table. 185 redoLogTable, err := testMetaStore.GetTable(redoLogTableName) 186 Ω(err).Should(BeNil()) 187 redoLogTableSchema := &memstore.TableSchema{ 188 Schema: *redoLogTable, 189 } 190 redoLogShard := memstore.NewTableShard(redoLogTableSchema, mockMetaStore, testDiskStore, CreateMockHostMemoryManger(), redoLogShardID) 191 192 mockShardNotExistErr := convertToAPIError(errors.New("Failed to get shard")) 193 memStore.On("GetTableShard", redoLogTableName, redoLogShardID).Return(redoLogShard, nil). 194 Run(func(arguments mock.Arguments) { 195 redoLogShard.Users.Add(1) 196 }) 197 memStore.On("GetTableShard", redoLogTableName, testTableShardID).Return(nil, mockShardNotExistErr) 198 memStore.On("GetTableShard", testTableName, 2).Return(nil, mockShardNotExistErr) 199 memStore.On("GetSchema", redoLogTableName).Return(redoLogTableSchema, nil) 200 201 scheduler = new(memMocks.Scheduler) 202 memStore.On("GetScheduler").Return(scheduler) 203 utils.SetClockImplementation(func() time.Time { 204 return time.Unix(100, 0) 205 }) 206 207 mockMetaStore.On( 208 "AddArchiveBatchVersion", 209 mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) 210 mockMetaStore.On( 211 "UpdateArchivingCutoff", mock.Anything, mock.Anything, 212 mock.Anything).Return(nil) 213 mockDiskStore.On( 214 "DeleteBatchVersions", mock.Anything, mock.Anything, 215 mock.Anything, mock.Anything, mock.Anything).Return(nil) 216 mockDiskStore.On( 217 "DeleteLogFile", mock.Anything, mock.Anything, 218 mock.Anything).Return(nil) 219 220 writer := new(utilsMocks.WriteCloser) 221 writer.On("Write", mock.Anything).Return(0, nil) 222 writer.On("Close").Return(nil) 223 mockDiskStore.On( 224 "OpenVectorPartyFileForWrite", mock.Anything, 225 mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(writer, nil) 226 227 queryHandler := NewQueryHandler(memStore, common.QueryConfig{ 228 DeviceMemoryUtilization: 0.9, 229 DeviceChoosingTimeout: 5, 230 }) 231 232 healthCheckHandler := NewHealthCheckHandler() 233 debugHandler = NewDebugHandler(memStore, mockMetaStore, queryHandler, healthCheckHandler) 234 testRouter := mux.NewRouter() 235 debugHandler.Register(testRouter.PathPrefix("/debug").Subrouter()) 236 testServer = httptest.NewUnstartedServer(testRouter) 237 testServer.Start() 238 }) 239 240 ginkgo.AfterEach(func() { 241 utils.ResetClockImplementation() 242 testServer.Close() 243 }) 244 245 ginkgo.It("Health", func() { 246 Ω(debugHandler.healthCheckHandler.disable).Should(BeFalse()) 247 hostPort := testServer.Listener.Addr().String() 248 249 resp, err := http.Get(fmt.Sprintf("http://%s/debug/health", hostPort)) 250 Ω(err).Should(BeNil()) 251 bs, err := ioutil.ReadAll(resp.Body) 252 Ω(resp.StatusCode).Should(Equal(200)) 253 Ω(string(bs)).Should(Equal("on")) 254 255 debugHandler.healthCheckHandler.disable = true 256 257 resp, err = http.Get(fmt.Sprintf("http://%s/debug/health", hostPort)) 258 Ω(err).Should(BeNil()) 259 bs, err = ioutil.ReadAll(resp.Body) 260 Ω(resp.StatusCode).Should(Equal(200)) 261 Ω(string(bs)).Should(Equal("off")) 262 }) 263 264 ginkgo.It("HealthSwitch", func() { 265 Ω(debugHandler.healthCheckHandler.disable).Should(BeFalse()) 266 hostPort := testServer.Listener.Addr().String() 267 268 resp, err := http.Post(fmt.Sprintf("http://%s/debug/health/off", hostPort), "", nil) 269 Ω(err).Should(BeNil()) 270 Ω(resp.StatusCode).Should(Equal(200)) 271 Ω(debugHandler.healthCheckHandler.disable).Should(BeTrue()) 272 273 resp, err = http.Post(fmt.Sprintf("http://%s/debug/health/on", hostPort), "", nil) 274 Ω(err).Should(BeNil()) 275 Ω(resp.StatusCode).Should(Equal(200)) 276 277 Ω(debugHandler.healthCheckHandler.disable).Should(BeFalse()) 278 279 resp, err = http.Post(fmt.Sprintf("http://%s/debug/health/os", hostPort), "", nil) 280 Ω(err).Should(BeNil()) 281 Ω(resp.StatusCode).Should(Equal(400)) 282 Ω(debugHandler.healthCheckHandler.disable).Should(BeFalse()) 283 }) 284 ginkgo.It("ShowBatch", func() { 285 hostPort := testServer.Listener.Addr().String() 286 resp, err := http.Get(fmt.Sprintf("http://%s/debug/%s/%d/batches/%d?startRow=0&numRows=10", hostPort, testTableName, testTableShardID, batchID)) 287 Ω(err).Should(BeNil()) 288 Ω(resp.StatusCode).Should(Equal(200)) 289 bs, err := ioutil.ReadAll(resp.Body) 290 var body ShowBatchResponse 291 json.Unmarshal(bs, &body.Body) 292 Ω(body.Body.Columns).Should(Equal([]string{"c0", "c1", "c2", "c3", "c4", "c5", "c6"})) 293 Ω(body.Body.NumRows).Should(Equal(5)) 294 Ω(body.Body.Vectors[0].Counts).Should(Equal([]int{1, 2, 3, 4, 5})) 295 Ω(body.Body.Vectors[1].Counts).Should(Equal([]int{3, 4, 5})) 296 Ω(body.Body.Vectors[2].Counts).Should(Equal([]int{1, 2, 3, 4, 5})) 297 Ω(body.Body.Vectors[3].Counts).Should(Equal([]int{5})) 298 Ω(body.Body.Vectors[4].Counts).Should(Equal([]int{1, 2, 3, 4, 5})) 299 Ω(body.Body.Vectors[5].Counts).Should(Equal([]int{5})) 300 301 // startRow and numRows should be optional 302 resp, _ = http.Get(fmt.Sprintf("http://%s/debug/%s/%d/batches/%d", hostPort, testTableName, testTableShardID, batchID)) 303 bs, _ = ioutil.ReadAll(resp.Body) 304 json.Unmarshal(bs, &body.Body) 305 Ω(resp.StatusCode).Should(Equal(200)) 306 Ω(body.Body.Columns).Should(Equal([]string{"c0", "c1", "c2", "c3", "c4", "c5", "c6"})) 307 Ω(body.Body.NumRows).Should(Equal(5)) 308 }) 309 310 ginkgo.It("show live batch should work", func() { 311 hostPort := testServer.Listener.Addr().String() 312 resp, err := http.Get(fmt.Sprintf("http://%s/debug/%s/%d/batches/%d?startRow=0&numRows=5", 313 hostPort, testTableName, testTableShardID, memstore.BaseBatchID)) 314 Ω(err).Should(BeNil()) 315 Ω(resp.StatusCode).Should(Equal(200)) 316 bs, err := ioutil.ReadAll(resp.Body) 317 var body ShowBatchResponse 318 json.Unmarshal(bs, &body.Body) 319 Ω(body.Body.Columns).Should(Equal([]string{"c0", "c1", "c2", "c3", "c4", "c5", "c6"})) 320 Ω(body.Body.NumRows).Should(Equal(1)) 321 Ω(body.Body.Vectors[0].Values).Should(Equal([]interface{}{nil})) 322 Ω(body.Body.Vectors[1].Values).Should(Equal([]interface{}{nil})) 323 Ω(body.Body.Vectors[2].Values).Should(Equal([]interface{}{nil})) 324 Ω(body.Body.Vectors[3].Values).Should(Equal([]interface{}{nil})) 325 Ω(body.Body.Vectors[4].Values).Should(Equal([]interface{}{nil})) 326 Ω(body.Body.Vectors[5].Values).Should(Equal([]interface{}{false})) 327 Ω(body.Body.Vectors[6].Values).Should(Equal([]interface{}{"enum case1"})) 328 329 // startRow and numRows should be optional 330 resp, _ = http.Get(fmt.Sprintf("http://%s/debug/%s/%d/batches/%d", hostPort, testTableName, testTableShardID, memstore.BaseBatchID)) 331 bs, _ = ioutil.ReadAll(resp.Body) 332 json.Unmarshal(bs, &body.Body) 333 Ω(resp.StatusCode).Should(Equal(200)) 334 Ω(body.Body.Columns).Should(Equal([]string{"c0", "c1", "c2", "c3", "c4", "c5", "c6"})) 335 Ω(body.Body.NumRows).Should(Equal(1)) 336 }) 337 338 ginkgo.It("LookupPrimaryKey", func() { 339 testSchema.PrimaryKeyBytes = 21 340 testSchema.PrimaryKeyColumnTypes = []memCom.DataType{memCom.UUID, memCom.Uint32, memCom.Bool} 341 hostPort := testServer.Listener.Addr().String() 342 resp, err := http.Get(fmt.Sprintf("http://%s/debug/%s/%d/primary-keys?key=01000000000000000100000000000000,1,true", hostPort, testTableName, testTableShardID)) 343 Ω(err).Should(BeNil()) 344 Ω(resp.StatusCode).Should(Equal(http.StatusOK)) 345 bs, _ := ioutil.ReadAll(resp.Body) 346 var r memstore.RecordID 347 json.Unmarshal(bs, &r) 348 Ω(r).Should(Equal(memstore.RecordID{ 349 BatchID: 1, 350 Index: 1, 351 })) 352 }) 353 354 ginkgo.It("Archiving request should work", func() { 355 hostPort := testServer.Listener.Addr().String() 356 request := &ArchiveRequest{} 357 request.Body.Cutoff = 200 358 job := new(memMocks.Job) 359 scheduler.On("NewArchivingJob", mock.Anything, mock.Anything, mock.Anything).Return(job) 360 scheduler.On("SubmitJob", job).Return(nil, nil) 361 job.On("Run", mock.Anything).Return(nil) 362 correctURL := fmt.Sprintf("http://%s/debug/%s/%d/archive", hostPort, testTableName, testTableShardID) 363 contentType := "application/json" 364 resp, err := http.Post(correctURL, contentType, RequestToBody(&request.Body)) 365 Ω(err).Should(BeNil()) 366 bs, err := ioutil.ReadAll(resp.Body) 367 Ω(err).Should(BeNil()) 368 Ω(resp.StatusCode).Should(Equal(http.StatusOK)) 369 Ω(string(bs)).Should(ContainSubstring("Archiving job submitted")) 370 371 // shard does not exist. 372 resp, err = http.Post( 373 fmt.Sprintf("http://%s/debug/%s/%d/archive", hostPort, testTableName, 2), contentType, 374 RequestToBody(&request.Body)) 375 Ω(err).Should(BeNil()) 376 bs, err = ioutil.ReadAll(resp.Body) 377 Ω(err).Should(BeNil()) 378 Ω(resp.StatusCode).Should(Equal(http.StatusBadRequest)) 379 Ω(string(bs)).Should(ContainSubstring("Failed to get shard")) 380 }) 381 382 ginkgo.It("ListRedoLogs should work", func() { 383 hostPort := testServer.Listener.Addr().String() 384 resp, err := http.Get( 385 fmt.Sprintf("http://%s/debug/%s/%d/redologs", hostPort, redoLogTableName, redoLogShardID)) 386 Ω(err).Should(BeNil()) 387 bs, err := ioutil.ReadAll(resp.Body) 388 Ω(err).Should(BeNil()) 389 Ω(resp.StatusCode).Should(Equal(http.StatusOK)) 390 391 var redoLogs []string 392 Ω(json.Unmarshal(bs, &redoLogs)).Should(BeNil()) 393 Ω(redoLogs).Should(ConsistOf(strconv.FormatInt(redoLogFile, 10))) 394 395 // Fail to get shard. 396 resp, err = http.Get( 397 fmt.Sprintf("http://%s/debug/%s/%d/redologs", hostPort, redoLogTableName, testTableShardID)) 398 Ω(err).Should(BeNil()) 399 bs, err = ioutil.ReadAll(resp.Body) 400 Ω(err).Should(BeNil()) 401 Ω(resp.StatusCode).Should(Equal(http.StatusBadRequest)) 402 Ω(string(bs)).Should(ContainSubstring("Failed to get shard")) 403 }) 404 405 ginkgo.It("ListUpsertBatches should work", func() { 406 hostPort := testServer.Listener.Addr().String() 407 resp, err := http.Get( 408 fmt.Sprintf("http://%s/debug/%s/%d/redologs/%d/upsertbatches", hostPort, redoLogTableName, 409 redoLogShardID, redoLogFile)) 410 Ω(err).Should(BeNil()) 411 bs, err := ioutil.ReadAll(resp.Body) 412 Ω(err).Should(BeNil()) 413 Ω(resp.StatusCode).Should(Equal(http.StatusOK)) 414 415 var respBody ListUpsertBatchesResponse 416 Ω(json.Unmarshal(bs, &respBody)).Should(BeNil()) 417 Ω(respBody).Should(ConsistOf(int64(4))) 418 419 // Fail to get shard. 420 resp, err = http.Get( 421 fmt.Sprintf("http://%s/debug/%s/%d/redologs/%d/upsertbatches", hostPort, redoLogTableName, 422 testTableShardID, redoLogFile)) 423 Ω(err).Should(BeNil()) 424 bs, err = ioutil.ReadAll(resp.Body) 425 Ω(err).Should(BeNil()) 426 Ω(resp.StatusCode).Should(Equal(http.StatusBadRequest)) 427 Ω(string(bs)).Should(ContainSubstring("Failed to get shard")) 428 429 // Redo log file does not exist. 430 resp, err = http.Get( 431 fmt.Sprintf("http://%s/debug/%s/%d/redologs/%d/upsertbatches", hostPort, redoLogTableName, 432 redoLogShardID, 1)) 433 Ω(err).Should(BeNil()) 434 bs, err = ioutil.ReadAll(resp.Body) 435 Ω(err).Should(BeNil()) 436 Ω(resp.StatusCode).Should(Equal(http.StatusInternalServerError)) 437 Ω(string(bs)).Should(ContainSubstring("Failed to open redolog file")) 438 }) 439 440 ginkgo.It("ReadUpsertBatch should work", func() { 441 expectedRows := [][]interface{}{{123, 0}, {234, 1}} 442 expectedColumnNames := []string{"c1", "c2"} 443 hostPort := testServer.Listener.Addr().String() 444 resp, err := http.Get( 445 fmt.Sprintf("http://%s/debug/%s/%d/redologs/%d/upsertbatches/%d"+ 446 "?draw=%d&start=%d&length=%d", 447 hostPort, redoLogTableName, redoLogShardID, redoLogFile, 4, 0, 0, 2)) 448 Ω(err).Should(BeNil()) 449 bs, err := ioutil.ReadAll(resp.Body) 450 Ω(err).Should(BeNil()) 451 Ω(resp.StatusCode).Should(Equal(http.StatusOK)) 452 453 respBody := ReadUpsertBatchResponse{ 454 Draw: 0, 455 Data: expectedRows, 456 ColumnNames: expectedColumnNames, 457 RecordsTotal: 2, 458 RecordsFiltered: 2, 459 } 460 461 jsonBytes, _ := json.Marshal(respBody) 462 Ω(string(bs)).Should(MatchJSON(string(jsonBytes))) 463 464 // shard does not exist 465 resp, err = http.Get( 466 fmt.Sprintf("http://%s/debug/%s/%d/redologs/%d/upsertbatches/%d"+ 467 "?draw=%d&start=%d&length=%d", 468 hostPort, redoLogTableName, testTableShardID, redoLogFile, 4, 0, 0, 2)) 469 Ω(err).Should(BeNil()) 470 bs, err = ioutil.ReadAll(resp.Body) 471 Ω(err).Should(BeNil()) 472 Ω(resp.StatusCode).Should(Equal(http.StatusBadRequest)) 473 Ω(string(bs)).Should(ContainSubstring("Failed to get shard")) 474 475 // Invalid offset. 476 resp, err = http.Get( 477 fmt.Sprintf("http://%s/debug/%s/%d/redologs/%d/upsertbatches/%d"+ 478 "?draw=%d&start=%d&length=%d", 479 hostPort, redoLogTableName, redoLogShardID, redoLogFile, 5, 0, 0, 2)) 480 Ω(err).Should(BeNil()) 481 bs, err = ioutil.ReadAll(resp.Body) 482 Ω(err).Should(BeNil()) 483 Ω(resp.StatusCode).Should(Equal(http.StatusInternalServerError)) 484 485 // Upsert batch row out of bound. 486 resp, err = http.Get( 487 fmt.Sprintf("http://%s/debug/%s/%d/redologs/%d/upsertbatches/%d"+ 488 "?draw=%d&start=%d&length=%d", 489 hostPort, redoLogTableName, redoLogShardID, redoLogFile, 4, 0, 2, 2)) 490 Ω(err).Should(BeNil()) 491 bs, err = ioutil.ReadAll(resp.Body) 492 Ω(err).Should(BeNil()) 493 Ω(resp.StatusCode).Should(Equal(http.StatusInternalServerError)) 494 Ω(string(bs)).Should(ContainSubstring("Invalid start or length")) 495 }) 496 497 ginkgo.It("ShowShardMeta request should work", func() { 498 hostPort := testServer.Listener.Addr().String() 499 resp, err := http.Get( 500 fmt.Sprintf("http://%s/debug/%s/%d", hostPort, testTableName, testTableShardID)) 501 Ω(err).Should(BeNil()) 502 bs, err := ioutil.ReadAll(resp.Body) 503 Ω(err).Should(BeNil()) 504 Ω(resp.StatusCode).Should(Equal(http.StatusOK)) 505 Ω(string(bs)).Should(MatchJSON(` 506 { 507 "schema": { 508 "schema": { 509 "name": "test", 510 "columns": [ 511 { 512 "name": "c0", 513 "type": "", 514 "config": {}, 515 "hllConfig": {} 516 }, 517 { 518 "name": "c1", 519 "type": "", 520 "config": {}, 521 "hllConfig": {} 522 }, 523 { 524 "name": "c2", 525 "type": "", 526 "config": {}, 527 "hllConfig": {} 528 }, 529 { 530 "name": "c3", 531 "type": "", 532 "config": {}, 533 "hllConfig": {} 534 }, 535 { 536 "name": "c4", 537 "type": "", 538 "config": {}, 539 "hllConfig": {} 540 }, 541 { 542 "name": "c5", 543 "type": "Bool", 544 "config": {}, 545 "hllConfig": {} 546 }, 547 { 548 "name": "c6", 549 "type": "SmallEnum", 550 "config": {}, 551 "hllConfig": {} 552 } 553 ], 554 "primaryKeyColumns": null, 555 "isFactTable": true, 556 "config": { 557 "batchSize": 10, 558 "backfillMaxBufferSize": 4294967296, 559 "backfillThresholdInBytes": 2097152 560 }, 561 "incarnation": 0, 562 "version": 0 563 }, 564 "columnIDs": { 565 "c0": 0, 566 "c1": 1, 567 "c2": 2, 568 "c3": 3, 569 "c4": 4, 570 "c5": 5, 571 "c6": 6 572 }, 573 "enumDicts": { 574 "c6": { 575 "capacity": 0, 576 "dict": null, 577 "reverseDict": [ 578 "enum case1" 579 ] 580 } 581 }, 582 "valueTypeByColumn": [ 583 0, 584 0, 585 0, 586 0, 587 0, 588 1, 589 524296 590 ], 591 "primaryKeyBytes": 0, 592 "primaryKeyColumnTypes": [] 593 }, 594 "liveStore": { 595 "backfillManager": { 596 "numRecords": 0, 597 "currentBufferSize": 0, 598 "backfillingBufferSize": 0, 599 "maxBufferSize": 4294967296, 600 "backfillThresholdInBytes": 2097152, 601 "lastRedoFile": 0, 602 "lastBatchOffset": 0, 603 "currentRedoFile": 0, 604 "currentBatchOffset": 0, 605 "numUpsertBatches": 0 606 }, 607 "batchSize": 10, 608 "batches": { 609 "-2147483648": { 610 "capacity": 10, 611 "numColumns": 7 612 } 613 }, 614 "lastModifiedTimePerColumn": null, 615 "lastReadRecord": { 616 "batchID": -2147483648, 617 "index": 1 618 }, 619 "nextWriteRecord": { 620 "batchID": -2147483648, 621 "index": 1 622 }, 623 "primaryKey": { 624 "allocatedBytes": 104000, 625 "capacity": 8000, 626 "eventTimeCutoff": 0, 627 "size": 1 628 }, 629 "redoLogManager": { 630 "rotationInterval": 0, 631 "maxRedoLogSize": 0, 632 "currentRedoLogSize": 0, 633 "totalRedologSize": 0, 634 "maxEventTimePerFile": {}, 635 "batchCountPerFile": {}, 636 "sizePerFile": {}, 637 "currentFileCreationTime": 0 638 }, 639 "snapshotManager": null 640 }, 641 "archiveStore": { 642 "currentVersion": { 643 "batches": { 644 "1": { 645 "numColumns": 6, 646 "size": 5, 647 "version": 0 648 } 649 }, 650 "archivingCutoff": 100 651 } 652 } 653 } 654 `)) 655 656 // shard does not exist. 657 resp, err = http.Get( 658 fmt.Sprintf("http://%s/debug/%s/%d", hostPort, testTableName, 2)) 659 Ω(err).Should(BeNil()) 660 bs, err = ioutil.ReadAll(resp.Body) 661 Ω(err).Should(BeNil()) 662 Ω(resp.StatusCode).Should(Equal(http.StatusBadRequest)) 663 }) 664 665 ginkgo.It("LoadVectorParty should work", func() { 666 hostPort := testServer.Listener.Addr().String() 667 columnName := "c0" 668 669 resp, err := http.Get(fmt.Sprintf("http://%s/debug/%s/%d/batches/%d/vector-parties/%s", hostPort, testTableName, testTableShardID, -1, columnName)) 670 Ω(err).Should(BeNil()) 671 Ω(resp.StatusCode).Should(Equal(http.StatusBadRequest)) 672 673 resp, err = http.Get(fmt.Sprintf("http://%s/debug/%s/%d/batches/%d/vector-parties/%s", hostPort, testTableName, testTableShardID, batchID, columnName)) 674 Ω(err).Should(BeNil()) 675 Ω(resp.StatusCode).Should(Equal(http.StatusOK)) 676 677 }) 678 679 ginkgo.It("EvictVectorParty should work", func() { 680 hostPort := testServer.Listener.Addr().String() 681 columnName := "c0" 682 683 request, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("http://%s/debug/%s/%d/batches/%d/vector-parties/%s", hostPort, testTableName, testTableShardID, -1, columnName), nil) 684 resp, err := http.DefaultClient.Do(request) 685 Ω(err).Should(BeNil()) 686 Ω(resp.StatusCode).Should(Equal(http.StatusBadRequest)) 687 688 request, err = http.NewRequest(http.MethodDelete, fmt.Sprintf("http://%s/debug/%s/%d/batches/%d/vector-parties/%s", hostPort, testTableName, testTableShardID, batchID, columnName), nil) 689 resp, err = http.DefaultClient.Do(request) 690 Ω(err).Should(BeNil()) 691 Ω(resp.StatusCode).Should(Equal(http.StatusOK)) 692 }) 693 694 ginkgo.It("ShowJobStatus for archiving job should work", func() { 695 expectedStatus := string(` 696 { 697 "test|1|archiving": { 698 "currentCutoff": 200, 699 "status": "succeeded", 700 "stage": "complete", 701 "runningCutoff": 200, 702 "nextRun": "0001-01-01T00:00:00Z", 703 "lastCutoff": 0, 704 "lastStartTime": "1970-01-01T00:01:40Z", 705 "lastRun": "0001-01-01T00:00:00Z" 706 } 707 } 708 `) 709 710 scheduler.On("RLock").Return() 711 scheduler.On("RUnlock").Return() 712 scheduler.On("GetJobDetails", memCom.ArchivingJobType).Return(map[string]*memstore.ArchiveJobDetail{ 713 "test|1|archiving": { 714 JobDetail: memstore.JobDetail{ 715 Status: memstore.JobSucceeded, 716 LastStartTime: time.Unix(100, 0).UTC(), 717 }, 718 CurrentCutoff: 200, 719 Stage: memstore.ArchivingComplete, 720 RunningCutoff: 200, 721 LastCutoff: 0, 722 }}) 723 hostPort := testServer.Listener.Addr().String() 724 resp, err := http.Get(fmt.Sprintf("http://%s/debug/jobs/archiving", hostPort)) 725 Ω(err).Should(BeNil()) 726 bs, err := ioutil.ReadAll(resp.Body) 727 Ω(err).Should(BeNil()) 728 Ω(resp.StatusCode).Should(Equal(http.StatusOK)) 729 730 var respBody map[string]*memstore.ArchiveJobDetail 731 Ω(json.Unmarshal(bs, &respBody)).Should(BeNil()) 732 Ω(bs).Should(MatchJSON(expectedStatus)) 733 }) 734 735 ginkgo.It("ShowJobStatus for backfill job should work", func() { 736 expectedStatus := string(` 737 { 738 "test|1|backfill": { 739 "status": "succeeded", 740 "stage": "complete", 741 "nextRun": "0001-01-01T00:00:00Z", 742 "lastStartTime": "1970-01-01T00:01:40Z", 743 "lastRun": "0001-01-01T00:00:00Z", 744 "redologFile": 0, 745 "batchOffset": 0 746 } 747 } 748 `) 749 750 scheduler.On("RLock").Return() 751 scheduler.On("RUnlock").Return() 752 scheduler.On("GetJobDetails", memCom.BackfillJobType).Return(map[string]*memstore.BackfillJobDetail{ 753 "test|1|backfill": { 754 JobDetail: memstore.JobDetail{ 755 Status: memstore.JobSucceeded, 756 LastStartTime: time.Unix(100, 0).UTC(), 757 }, 758 Stage: memstore.BackfillComplete, 759 }}) 760 hostPort := testServer.Listener.Addr().String() 761 resp, err := http.Get(fmt.Sprintf("http://%s/debug/jobs/backfill", hostPort)) 762 Ω(err).Should(BeNil()) 763 bs, err := ioutil.ReadAll(resp.Body) 764 Ω(err).Should(BeNil()) 765 Ω(resp.StatusCode).Should(Equal(http.StatusOK)) 766 767 var respBody map[string]*memstore.BackfillJobDetail 768 Ω(json.Unmarshal(bs, &respBody)).Should(BeNil()) 769 Ω(bs).Should(MatchJSON(expectedStatus)) 770 }) 771 772 ginkgo.It("ShowJobStatus for snapshot job should work", func() { 773 expectedStatus := string(` 774 { 775 "test|1|snapshot": { 776 "status": "succeeded", 777 "nextRun": "0001-01-01T00:00:00Z", 778 "lastRun": "0001-01-01T00:00:00Z", 779 "lastStartTime": "1970-01-01T00:01:40Z", 780 "numMutations": 0, 781 "numBatches": 0, 782 "redologFile": 0, 783 "batchOffset": 0, 784 "stage": "" 785 } 786 } 787 `) 788 789 scheduler.On("RLock").Return() 790 scheduler.On("RUnlock").Return() 791 scheduler.On("GetJobDetails", memCom.SnapshotJobType).Return(map[string]*memstore.SnapshotJobDetail{ 792 "test|1|snapshot": { 793 JobDetail: memstore.JobDetail{ 794 Status: memstore.JobSucceeded, 795 LastStartTime: time.Unix(100, 0).UTC(), 796 }, 797 }}) 798 hostPort := testServer.Listener.Addr().String() 799 resp, err := http.Get(fmt.Sprintf("http://%s/debug/jobs/snapshot", hostPort)) 800 Ω(err).Should(BeNil()) 801 bs, err := ioutil.ReadAll(resp.Body) 802 Ω(err).Should(BeNil()) 803 Ω(resp.StatusCode).Should(Equal(http.StatusOK)) 804 805 var respBody map[string]*memstore.SnapshotJobDetail 806 Ω(json.Unmarshal(bs, &respBody)).Should(BeNil()) 807 Ω(bs).Should(MatchJSON(expectedStatus)) 808 }) 809 810 ginkgo.It("ShowHostMemory should work", func() { 811 memoryUsages := map[string]memstore.TableShardMemoryUsage{ 812 "table1": { 813 PrimaryKeyMemory: 10, 814 }, 815 } 816 817 expectedResponse := string(` 818 { 819 "table1": { 820 "cols": null, 821 "pk": 10 822 } 823 } 824 `) 825 826 memStore.On("GetMemoryUsageDetails").Return(memoryUsages, nil) 827 828 hostPort := testServer.Listener.Addr().String() 829 resp, err := http.Get(fmt.Sprintf("http://%s/debug/host-memory", hostPort)) 830 Ω(err).Should(BeNil()) 831 bs, err := ioutil.ReadAll(resp.Body) 832 Ω(err).Should(BeNil()) 833 Ω(resp.StatusCode).Should(Equal(http.StatusOK)) 834 835 var respBody map[string]memstore.TableShardMemoryUsage 836 Ω(json.Unmarshal(bs, &respBody)).Should(BeNil()) 837 Ω(bs).Should(MatchJSON(expectedResponse)) 838 }) 839 840 ginkgo.It("ReadBackfillQueueUpsertBatch should work", func() { 841 builder := memCom.NewUpsertBatchBuilder() 842 builder.AddRow() 843 err := builder.AddColumn(1, memCom.Uint8) 844 Ω(err).Should(BeNil()) 845 builder.SetValue(0, 0, uint8(135)) 846 buffer, err := builder.ToByteArray() 847 Ω(err).Should(BeNil()) 848 upsertBatch, err := memstore.NewUpsertBatch(buffer) 849 Ω(upsertBatch).ShouldNot(BeNil()) 850 Ω(err).Should(BeNil()) 851 852 testShard, _ := memStore.GetTableShard(testTableName, testTableShardID) 853 Ω(testShard.LiveStore.BackfillManager.Append(upsertBatch, 0, 0)).Should(BeFalse()) 854 expectedRows := [][]interface{}{{135}} 855 expectedColumnNames := []string{"c1"} 856 hostPort := testServer.Listener.Addr().String() 857 resp, err := http.Get( 858 fmt.Sprintf("http://%s/debug/%s/%d/backfill-manager/upsertbatches/%d"+ 859 "?draw=%d&start=%d&length=%d", 860 hostPort, testTableName, testTableShardID, 0, 0, 0, 2)) 861 Ω(err).Should(BeNil()) 862 bs, err := ioutil.ReadAll(resp.Body) 863 Ω(err).Should(BeNil()) 864 Ω(resp.StatusCode).Should(Equal(http.StatusOK)) 865 866 respBody := ReadUpsertBatchResponse{ 867 Draw: 0, 868 Data: expectedRows, 869 ColumnNames: expectedColumnNames, 870 RecordsTotal: 1, 871 RecordsFiltered: 1, 872 } 873 874 jsonBytes, _ := json.Marshal(respBody) 875 Ω(string(bs)).Should(MatchJSON(string(jsonBytes))) 876 877 // shard does not exist 878 resp, err = http.Get( 879 fmt.Sprintf("http://%s/debug/%s/%d/backfill-manager/upsertbatches/%d"+ 880 "?draw=%d&start=%d&length=%d", 881 hostPort, redoLogTableName, testTableShardID, 0, 0, 0, 2)) 882 Ω(err).Should(BeNil()) 883 bs, err = ioutil.ReadAll(resp.Body) 884 Ω(err).Should(BeNil()) 885 Ω(resp.StatusCode).Should(Equal(http.StatusBadRequest)) 886 Ω(string(bs)).Should(ContainSubstring("Failed to get shard")) 887 }) 888 889 ginkgo.It("ShowDeviceStatus should work", func() { 890 expectedStatus := string(` 891 { 892 "deviceInfos": [ 893 { 894 "deviceID": 0, 895 "queryCount": 0, 896 "totalMemory": 25576865792, 897 "totalAvailableMemory": 23019177984, 898 "totalFreeMemory": 23019177984 899 } 900 ], 901 "timeout": 5, 902 "maxAvailableMemory": 23019177984 903 } 904 `) 905 906 hostPort := testServer.Listener.Addr().String() 907 resp, err := http.Get(fmt.Sprintf("http://%s/debug/devices", hostPort)) 908 Ω(err).Should(BeNil()) 909 bs, err := ioutil.ReadAll(resp.Body) 910 Ω(err).Should(BeNil()) 911 Ω(resp.StatusCode).Should(Equal(http.StatusOK)) 912 913 var respBody query.DeviceManager 914 Ω(json.Unmarshal(bs, &respBody)).Should(BeNil()) 915 Ω(bs).Should(MatchJSON(expectedStatus)) 916 }) 917 918 ginkgo.It("Backfill request should work", func() { 919 hostPort := testServer.Listener.Addr().String() 920 request := &BackfillRequest{} 921 bs, err := json.Marshal(request) 922 Ω(err).Should(BeNil()) 923 job := new(memMocks.Job) 924 scheduler.On("NewBackfillJob", mock.Anything, mock.Anything, mock.Anything).Return(job) 925 scheduler.On("SubmitJob", job).Return(nil, nil) 926 job.On("Run", mock.Anything).Return(nil) 927 correctURL := fmt.Sprintf("http://%s/debug/%s/%d/backfill", hostPort, testTableName, testTableShardID) 928 contentType := "application/json" 929 resp, err := http.Post(correctURL, contentType, bytes.NewReader(bs)) 930 Ω(err).Should(BeNil()) 931 bs, err = ioutil.ReadAll(resp.Body) 932 Ω(err).Should(BeNil()) 933 Ω(resp.StatusCode).Should(Equal(http.StatusOK)) 934 Ω(string(bs)).Should(ContainSubstring("Backfill job submitted")) 935 936 // shard does not exist. 937 resp, err = http.Post( 938 fmt.Sprintf("http://%s/debug/%s/%d/backfill", hostPort, testTableName, 2), contentType, nil) 939 Ω(err).Should(BeNil()) 940 bs, err = ioutil.ReadAll(resp.Body) 941 Ω(err).Should(BeNil()) 942 Ω(resp.StatusCode).Should(Equal(http.StatusBadRequest)) 943 Ω(string(bs)).Should(ContainSubstring("Failed to get shard")) 944 }) 945 946 ginkgo.It("Snapshot request should work", func() { 947 hostPort := testServer.Listener.Addr().String() 948 request := &SnapshotRequest{} 949 bs, err := json.Marshal(request) 950 job := new(memMocks.Job) 951 scheduler.On("NewSnapshotJob", mock.Anything, mock.Anything, mock.Anything).Return(job) 952 scheduler.On("SubmitJob", job).Return(nil, nil) 953 job.On("Run", mock.Anything).Return(nil) 954 correctURL := fmt.Sprintf("http://%s/debug/%s/%d/snapshot", hostPort, testTableName, testTableShardID) 955 contentType := "application/json" 956 resp, err := http.Post(correctURL, contentType, bytes.NewReader(bs)) 957 Ω(err).Should(BeNil()) 958 bs, err = ioutil.ReadAll(resp.Body) 959 Ω(err).Should(BeNil()) 960 Ω(resp.StatusCode).Should(Equal(http.StatusOK)) 961 Ω(string(bs)).Should(ContainSubstring("Snapshot job submitted")) 962 963 // shard does not exist. 964 resp, err = http.Post( 965 fmt.Sprintf("http://%s/debug/%s/%d/snapshot", hostPort, testTableName, 2), contentType, nil) 966 Ω(err).Should(BeNil()) 967 bs, err = ioutil.ReadAll(resp.Body) 968 Ω(err).Should(BeNil()) 969 Ω(resp.StatusCode).Should(Equal(http.StatusBadRequest)) 970 Ω(string(bs)).Should(ContainSubstring("Failed to get shard")) 971 }) 972 973 ginkgo.It("Purge request should work", func() { 974 hostPort := testServer.Listener.Addr().String() 975 request := &PurgeRequest{} 976 bs, err := json.Marshal(request) 977 job := new(memMocks.Job) 978 scheduler.On("NewPurgeJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(job) 979 scheduler.On("SubmitJob", job).Return(nil, nil) 980 job.On("Run", mock.Anything).Return(nil) 981 correctURL := fmt.Sprintf("http://%s/debug/%s/%d/purge", hostPort, testTableName, testTableShardID) 982 contentType := "application/json" 983 resp, err := http.Post(correctURL, contentType, RequestToBody(&request.Body)) 984 Ω(err).Should(BeNil()) 985 bs, err = ioutil.ReadAll(resp.Body) 986 Ω(err).Should(BeNil()) 987 Ω(resp.StatusCode).Should(Equal(http.StatusOK)) 988 Ω(string(bs)).Should(ContainSubstring("Purge job submitted")) 989 990 // shard does not exist. 991 resp, err = http.Post( 992 fmt.Sprintf("http://%s/debug/%s/%d/purge", hostPort, testTableName, 2), contentType, RequestToBody(&request.Body)) 993 Ω(err).Should(BeNil()) 994 bs, err = ioutil.ReadAll(resp.Body) 995 Ω(err).Should(BeNil()) 996 Ω(resp.StatusCode).Should(Equal(http.StatusBadRequest)) 997 Ω(string(bs)).Should(ContainSubstring("Failed to get shard")) 998 999 testSchema.Schema.Config.RecordRetentionInDays = 0 1000 request.Body.SafePurge = true 1001 resp, err = http.Post( 1002 fmt.Sprintf("http://%s/debug/%s/%d/purge", hostPort, testTableName, 1), contentType, RequestToBody(&request.Body)) 1003 Ω(err).Should(BeNil()) 1004 bs, err = ioutil.ReadAll(resp.Body) 1005 Ω(err).Should(BeNil()) 1006 Ω(resp.StatusCode).Should(Equal(http.StatusBadRequest)) 1007 Ω(string(bs)).Should(ContainSubstring("safe purge attempted on table with infinite retention")) 1008 1009 testSchema.Schema.Config.RecordRetentionInDays = 10 1010 request.Body.SafePurge = true 1011 resp, err = http.Post( 1012 fmt.Sprintf("http://%s/debug/%s/%d/purge", hostPort, testTableName, 1), contentType, RequestToBody(&request.Body)) 1013 Ω(err).Should(BeNil()) 1014 bs, err = ioutil.ReadAll(resp.Body) 1015 Ω(err).Should(BeNil()) 1016 Ω(resp.StatusCode).Should(Equal(http.StatusOK)) 1017 }) 1018 })