github.com/nutsdb/nutsdb@v1.0.4/tx_list_test.go (about) 1 // Copyright 2019 The nutsdb Author. All rights reserved. 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 nutsdb 16 17 import ( 18 "testing" 19 "time" 20 21 "github.com/stretchr/testify/require" 22 ) 23 24 func pushDataByStartEnd(t *testing.T, db *DB, bucket string, key int, start, end int, isLeft bool) { 25 for i := start; i <= end; i++ { 26 txPush(t, db, bucket, GetTestBytes(key), GetTestBytes(i), isLeft, nil, nil) 27 } 28 } 29 30 func pushDataByValues(t *testing.T, db *DB, bucket string, key int, isLeft bool, values ...int) { 31 for _, v := range values { 32 txPush(t, db, bucket, GetTestBytes(key), GetTestBytes(v), isLeft, nil, nil) 33 } 34 } 35 36 func TestTx_RPush(t *testing.T) { 37 bucket := "bucket" 38 39 // 1. Insert values for some keys by using RPush 40 // 2. Validate values for these keys by using RPop 41 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 42 txCreateBucket(t, db, DataStructureList, bucket, nil) 43 pushDataByStartEnd(t, db, bucket, 0, 0, 9, false) 44 pushDataByStartEnd(t, db, bucket, 1, 10, 19, false) 45 pushDataByStartEnd(t, db, bucket, 2, 20, 29, false) 46 47 for i := 0; i < 10; i++ { 48 txPop(t, db, bucket, GetTestBytes(0), GetTestBytes(9-i), nil, false) 49 } 50 for i := 10; i < 20; i++ { 51 txPop(t, db, bucket, GetTestBytes(1), GetTestBytes(29-i), nil, false) 52 } 53 for i := 20; i < 30; i++ { 54 txPop(t, db, bucket, GetTestBytes(2), GetTestBytes(49-i), nil, false) 55 } 56 }) 57 } 58 59 func TestTx_MPush(t *testing.T) { 60 bucket := "bucket" 61 t.Run("Test Multiple LPush ", func(t *testing.T) { 62 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 63 bbs := make([][]byte, 0) 64 bbs = append(bbs, GetTestBytes(2)) 65 bbs = append(bbs, GetTestBytes(3)) 66 bbs = append(bbs, GetTestBytes(4)) 67 68 txCreateBucket(t, db, DataStructureList, bucket, nil) 69 txMPush(t, db, bucket, GetTestBytes(1), bbs, true, nil, nil) 70 71 expect := make([][]byte, 0) 72 for i := len(bbs) - 1; i >= 0; i-- { 73 expect = append(expect, bbs[i]) 74 } 75 76 txLRange(t, db, bucket, GetTestBytes(1), 0, 2, 3, expect, nil) 77 }) 78 }) 79 80 t.Run("Test Error LPush ", func(t *testing.T) { 81 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 82 bbs := make([][]byte, 0) 83 bbs = append(bbs, GetTestBytes(2)) 84 bbs = append(bbs, GetTestBytes(3)) 85 bbs = append(bbs, GetTestBytes(4)) 86 87 txCreateBucket(t, db, DataStructureList, bucket, nil) 88 txMPush(t, db, "test1", GetTestBytes(1), bbs, true, ErrorBucketNotExist, nil) 89 }) 90 }) 91 92 t.Run("Test Multiple RPush ", func(t *testing.T) { 93 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 94 bbs := make([][]byte, 0) 95 bbs = append(bbs, GetTestBytes(2)) 96 bbs = append(bbs, GetTestBytes(3)) 97 bbs = append(bbs, GetTestBytes(4)) 98 txCreateBucket(t, db, DataStructureList, bucket, nil) 99 txMPush(t, db, bucket, GetTestBytes(1), bbs, false, nil, nil) 100 txLRange(t, db, bucket, GetTestBytes(1), 0, 2, 3, bbs, nil) 101 }) 102 }) 103 104 t.Run("Test Error RPush ", func(t *testing.T) { 105 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 106 bbs := make([][]byte, 0) 107 bbs = append(bbs, GetTestBytes(2)) 108 bbs = append(bbs, GetTestBytes(3)) 109 bbs = append(bbs, GetTestBytes(4)) 110 111 txCreateBucket(t, db, DataStructureList, bucket, nil) 112 txMPush(t, db, "test1", GetTestBytes(1), bbs, false, ErrorBucketNotExist, nil) 113 }) 114 }) 115 } 116 117 func TestTx_LPush(t *testing.T) { 118 bucket := "bucket" 119 120 // 1. Insert values for some keys by using LPush 121 // 2. Validate values for these keys by using LPop 122 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 123 txCreateBucket(t, db, DataStructureList, bucket, nil) 124 125 pushDataByStartEnd(t, db, bucket, 0, 0, 9, true) 126 pushDataByStartEnd(t, db, bucket, 1, 10, 19, true) 127 pushDataByStartEnd(t, db, bucket, 2, 20, 29, true) 128 129 txPush(t, db, bucket, []byte("012|sas"), GetTestBytes(0), true, ErrSeparatorForListKey, nil) 130 131 for i := 0; i < 10; i++ { 132 txPop(t, db, bucket, GetTestBytes(0), GetTestBytes(9-i), nil, true) 133 } 134 for i := 10; i < 20; i++ { 135 txPop(t, db, bucket, GetTestBytes(1), GetTestBytes(29-i), nil, true) 136 } 137 for i := 20; i < 30; i++ { 138 txPop(t, db, bucket, GetTestBytes(2), GetTestBytes(49-i), nil, true) 139 } 140 }) 141 } 142 143 func TestTx_LPushRaw(t *testing.T) { 144 bucket := "bucket" 145 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 146 txCreateBucket(t, db, DataStructureList, bucket, nil) 147 148 seq := uint64(100000) 149 for i := 0; i <= 100; i++ { 150 key := encodeListKey([]byte("0"), seq) 151 seq-- 152 txPushRaw(t, db, bucket, key, GetTestBytes(i), true, nil, nil) 153 } 154 155 for i := 0; i <= 100; i++ { 156 v := GetTestBytes(100 - i) 157 txPop(t, db, bucket, []byte("0"), v, nil, true) 158 } 159 }) 160 } 161 162 func TestTx_RPushRaw(t *testing.T) { 163 bucket := "bucket" 164 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 165 txCreateBucket(t, db, DataStructureList, bucket, nil) 166 seq := uint64(100000) 167 for i := 0; i <= 100; i++ { 168 key := encodeListKey([]byte("0"), seq) 169 seq++ 170 txPushRaw(t, db, bucket, key, GetTestBytes(i), false, nil, nil) 171 } 172 173 txPush(t, db, bucket, []byte("012|sas"), GetTestBytes(0), false, ErrSeparatorForListKey, nil) 174 175 for i := 0; i <= 100; i++ { 176 v := GetTestBytes(100 - i) 177 txPop(t, db, bucket, []byte("0"), v, nil, false) 178 } 179 }) 180 } 181 182 func TestTx_LPop(t *testing.T) { 183 bucket := "bucket" 184 185 // Calling LPop on a non-existent list 186 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 187 txCreateBucket(t, db, DataStructureList, bucket, nil) 188 txPop(t, db, bucket, GetTestBytes(0), nil, ErrListNotFound, true) 189 }) 190 191 // Insert some values for a key and validate them by using LPop 192 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 193 txCreateBucket(t, db, DataStructureList, bucket, nil) 194 pushDataByStartEnd(t, db, bucket, 0, 0, 2, true) 195 for i := 0; i < 3; i++ { 196 txPop(t, db, bucket, GetTestBytes(0), GetTestBytes(2-i), nil, true) 197 } 198 }) 199 } 200 201 func TestTx_RPop(t *testing.T) { 202 bucket := "bucket" 203 204 // Calling RPop on a non-existent list 205 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 206 txCreateBucket(t, db, DataStructureList, bucket, nil) 207 txPop(t, db, bucket, GetTestBytes(0), nil, ErrListNotFound, false) 208 }) 209 210 // Calling RPop on a list with added data 211 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 212 txCreateBucket(t, db, DataStructureList, bucket, nil) 213 pushDataByStartEnd(t, db, bucket, 0, 0, 2, false) 214 215 txPop(t, db, "fake_bucket", GetTestBytes(0), nil, ErrBucketNotExist, false) 216 217 for i := 0; i < 3; i++ { 218 txPop(t, db, bucket, GetTestBytes(0), GetTestBytes(2-i), nil, false) 219 } 220 221 txPop(t, db, bucket, GetTestBytes(0), nil, ErrEmptyList, false) 222 }) 223 } 224 225 func TestTx_LRange(t *testing.T) { 226 bucket := "bucket" 227 228 // Calling LRange on a non-existent list 229 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 230 txCreateBucket(t, db, DataStructureList, bucket, nil) 231 232 txLRange(t, db, bucket, GetTestBytes(0), 0, -1, 0, nil, ErrListNotFound) 233 }) 234 235 // Calling LRange on a list with added data 236 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 237 txCreateBucket(t, db, DataStructureList, bucket, nil) 238 239 pushDataByStartEnd(t, db, bucket, 0, 0, 2, true) 240 241 txLRange(t, db, bucket, GetTestBytes(0), 0, -1, 3, [][]byte{ 242 GetTestBytes(2), GetTestBytes(1), GetTestBytes(0), 243 }, nil) 244 245 for i := 0; i < 3; i++ { 246 txPop(t, db, bucket, GetTestBytes(0), GetTestBytes(2-i), nil, true) 247 } 248 249 txLRange(t, db, bucket, GetTestBytes(0), 0, -1, 0, nil, nil) 250 }) 251 } 252 253 func TestTx_LRem(t *testing.T) { 254 bucket := "bucket" 255 256 // Calling LRem on a non-existent list 257 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 258 txCreateBucket(t, db, DataStructureList, bucket, nil) 259 txLRem(t, db, bucket, GetTestBytes(0), 1, GetTestBytes(0), ErrListNotFound) 260 }) 261 262 // A basic calling for LRem 263 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 264 txCreateBucket(t, db, DataStructureList, bucket, nil) 265 266 pushDataByStartEnd(t, db, bucket, 0, 0, 3, true) 267 268 txLRem(t, db, bucket, GetTestBytes(0), 1, GetTestBytes(0), nil) 269 txLRange(t, db, bucket, GetTestBytes(0), 0, -1, 3, [][]byte{ 270 GetTestBytes(3), GetTestBytes(2), GetTestBytes(1), 271 }, nil) 272 txLRem(t, db, bucket, GetTestBytes(0), 4, GetTestBytes(0), ErrCount) 273 txLRem(t, db, bucket, GetTestBytes(0), 1, GetTestBytes(1), nil) 274 }) 275 276 // Calling LRem with count > 0 277 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 278 txCreateBucket(t, db, DataStructureList, bucket, nil) 279 280 count := 3 281 282 pushDataByValues(t, db, bucket, 1, true, 0, 1, 0, 1, 0, 1, 0, 1) 283 284 txLRange(t, db, bucket, GetTestBytes(1), 0, -1, 8, [][]byte{ 285 GetTestBytes(1), GetTestBytes(0), GetTestBytes(1), GetTestBytes(0), 286 GetTestBytes(1), GetTestBytes(0), GetTestBytes(1), GetTestBytes(0), 287 }, nil) 288 txLRem(t, db, bucket, GetTestBytes(1), count, GetTestBytes(0), nil) 289 txLRange(t, db, bucket, GetTestBytes(1), 0, -1, 5, [][]byte{ 290 GetTestBytes(1), GetTestBytes(1), GetTestBytes(1), GetTestBytes(1), GetTestBytes(0), 291 }, nil) 292 }) 293 294 // Calling LRem with count == 0 295 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 296 txCreateBucket(t, db, DataStructureList, bucket, nil) 297 count := 0 298 299 pushDataByValues(t, db, bucket, 1, true, 0, 1, 0, 1, 0, 1, 0, 1) 300 301 txLRange(t, db, bucket, GetTestBytes(1), 0, -1, 8, [][]byte{ 302 GetTestBytes(1), GetTestBytes(0), GetTestBytes(1), GetTestBytes(0), 303 GetTestBytes(1), GetTestBytes(0), GetTestBytes(1), GetTestBytes(0), 304 }, nil) 305 txLRem(t, db, bucket, GetTestBytes(1), count, GetTestBytes(0), nil) 306 txLRange(t, db, bucket, GetTestBytes(1), 0, -1, 4, [][]byte{ 307 GetTestBytes(1), GetTestBytes(1), GetTestBytes(1), GetTestBytes(1), 308 }, nil) 309 }) 310 311 // Calling LRem with count < 0 312 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 313 txCreateBucket(t, db, DataStructureList, bucket, nil) 314 315 count := -3 316 317 pushDataByValues(t, db, bucket, 1, true, 0, 1, 0, 1, 0, 1, 0, 1) 318 319 txLRange(t, db, bucket, GetTestBytes(1), 0, -1, 8, [][]byte{ 320 GetTestBytes(1), GetTestBytes(0), GetTestBytes(1), GetTestBytes(0), 321 GetTestBytes(1), GetTestBytes(0), GetTestBytes(1), GetTestBytes(0), 322 }, nil) 323 txLRem(t, db, bucket, GetTestBytes(1), count, GetTestBytes(0), nil) 324 txLRange(t, db, bucket, GetTestBytes(1), 0, -1, 5, [][]byte{ 325 GetTestBytes(1), GetTestBytes(0), GetTestBytes(1), GetTestBytes(1), GetTestBytes(1), 326 }, nil) 327 }) 328 } 329 330 func TestTx_LTrim(t *testing.T) { 331 bucket := "bucket" 332 333 // Calling LTrim on a non-existent list 334 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 335 txCreateBucket(t, db, DataStructureList, bucket, nil) 336 txLTrim(t, db, bucket, GetTestBytes(0), 0, 1, ErrListNotFound) 337 }) 338 339 // Calling LTrim on a list with added data and use LRange to validate it 340 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 341 txCreateBucket(t, db, DataStructureList, bucket, nil) 342 pushDataByStartEnd(t, db, bucket, 0, 0, 2, true) 343 txLTrim(t, db, bucket, GetTestBytes(0), 0, 1, nil) 344 345 txLRange(t, db, bucket, GetTestBytes(0), 0, -1, 2, [][]byte{ 346 GetTestBytes(2), GetTestBytes(1), 347 }, nil) 348 }) 349 350 // Calling LTrim with incorrect start and end 351 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 352 txCreateBucket(t, db, DataStructureList, bucket, nil) 353 354 for i := 0; i < 3; i++ { 355 txPush(t, db, bucket, GetTestBytes(2), GetTestBytes(i), true, nil, nil) 356 } 357 txLTrim(t, db, bucket, GetTestBytes(2), 0, -10, ErrStartOrEnd) 358 }) 359 } 360 361 func TestTx_LSize(t *testing.T) { 362 bucket := "bucket" 363 364 // Calling LSize on a non-existent list 365 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 366 txCreateBucket(t, db, DataStructureList, bucket, nil) 367 txLSize(t, db, bucket, GetTestBytes(0), 0, ErrListNotFound) 368 }) 369 370 // Calling LSize after adding some values 371 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 372 txCreateBucket(t, db, DataStructureList, bucket, nil) 373 pushDataByStartEnd(t, db, bucket, 0, 0, 2, false) 374 txLSize(t, db, bucket, GetTestBytes(0), 3, nil) 375 }) 376 } 377 378 func TestTx_LRemByIndex(t *testing.T) { 379 bucket := "bucket" 380 381 // Calling LRemByIndex on a newly created empty list 382 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 383 txCreateBucket(t, db, DataStructureList, bucket, nil) 384 txLRemByIndex(t, db, bucket, GetTestBytes(0), nil) 385 }) 386 387 // Calling LRemByIndex with len(indexes) == 0 388 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 389 txCreateBucket(t, db, DataStructureList, bucket, nil) 390 pushDataByValues(t, db, bucket, 0, true, 0) 391 txLRemByIndex(t, db, bucket, GetTestBytes(0), nil) 392 }) 393 394 // Calling LRemByIndex with a expired bucket name 395 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 396 txCreateBucket(t, db, DataStructureList, bucket, nil) 397 pushDataByValues(t, db, bucket, 0, true, 0) 398 txExpireList(t, db, bucket, GetTestBytes(0), 1, nil) 399 time.Sleep(3 * time.Second) 400 txLRemByIndex(t, db, bucket, GetTestBytes(0), ErrListNotFound) 401 }) 402 403 // Calling LRemByIndex on a list with added data and use LRange to validate it 404 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 405 txCreateBucket(t, db, DataStructureList, bucket, nil) 406 pushDataByStartEnd(t, db, bucket, 0, 0, 2, false) 407 txLRemByIndex(t, db, bucket, GetTestBytes(0), nil, 1, 0, 8, -8, 88, -88) 408 txLRange(t, db, bucket, GetTestBytes(0), 0, -1, 1, [][]byte{ 409 GetTestBytes(2), 410 }, nil) 411 }) 412 } 413 414 func TestTx_ExpireList(t *testing.T) { 415 bucket := "bucket" 416 417 // Verify that the list with expiration time expires normally 418 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 419 txCreateBucket(t, db, DataStructureList, bucket, nil) 420 pushDataByStartEnd(t, db, bucket, 0, 0, 3, false) 421 txLRange(t, db, bucket, GetTestBytes(0), 0, -1, 4, [][]byte{ 422 GetTestBytes(0), GetTestBytes(1), GetTestBytes(2), GetTestBytes(3), 423 }, nil) 424 425 txExpireList(t, db, bucket, GetTestBytes(0), 1, nil) 426 time.Sleep(time.Second) 427 txLRange(t, db, bucket, GetTestBytes(0), 0, -1, 0, nil, ErrListNotFound) 428 }) 429 430 // Verify that the list with persistent time 431 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 432 txCreateBucket(t, db, DataStructureList, bucket, nil) 433 pushDataByStartEnd(t, db, bucket, 0, 0, 3, false) 434 txExpireList(t, db, bucket, GetTestBytes(0), Persistent, nil) 435 time.Sleep(time.Second) 436 txLRange(t, db, bucket, GetTestBytes(0), 0, -1, 4, [][]byte{ 437 GetTestBytes(0), GetTestBytes(1), GetTestBytes(2), GetTestBytes(3), 438 }, nil) 439 }) 440 } 441 442 func TestTx_LKeys(t *testing.T) { 443 bucket := "bucket" 444 445 // Calling LKeys after adding some keys 446 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 447 txCreateBucket(t, db, DataStructureList, bucket, nil) 448 pushDataByValues(t, db, bucket, 10, false, 0) 449 pushDataByValues(t, db, bucket, 11, false, 1) 450 pushDataByValues(t, db, bucket, 12, false, 2) 451 pushDataByValues(t, db, bucket, 23, false, 3) 452 453 txLKeys(t, db, bucket, "*", 4, nil, func(keys []string) bool { 454 return true 455 }) 456 457 txLKeys(t, db, bucket, "*", 2, nil, func(keys []string) bool { 458 return len(keys) != 2 459 }) 460 461 txLKeys(t, db, bucket, "nutsdb-00000001*", 3, nil, func(keys []string) bool { 462 return true 463 }) 464 }) 465 } 466 467 func TestTx_GetListTTL(t *testing.T) { 468 bucket := "bucket" 469 470 // Verify TTL of list 471 runNutsDBTest(t, nil, func(t *testing.T, db *DB) { 472 txCreateBucket(t, db, DataStructureList, bucket, nil) 473 pushDataByStartEnd(t, db, bucket, 0, 0, 3, false) 474 475 txGetListTTL(t, db, bucket, GetTestBytes(0), uint32(0), nil) 476 txExpireList(t, db, bucket, GetTestBytes(0), uint32(1), nil) 477 txGetListTTL(t, db, bucket, GetTestBytes(0), uint32(1), nil) 478 479 time.Sleep(3 * time.Second) 480 txGetListTTL(t, db, bucket, GetTestBytes(0), uint32(0), ErrListNotFound) 481 }) 482 } 483 484 func TestTx_ListEntryIdxMode_HintKeyValAndRAMIdxMode(t *testing.T) { 485 bucket := "bucket" 486 key := GetTestBytes(0) 487 488 opts := DefaultOptions 489 opts.EntryIdxMode = HintKeyValAndRAMIdxMode 490 491 // HintKeyValAndRAMIdxMode 492 runNutsDBTest(t, &opts, func(t *testing.T, db *DB) { 493 txCreateBucket(t, db, DataStructureList, bucket, nil) 494 err := db.Update(func(tx *Tx) error { 495 err := tx.LPush(bucket, key, []byte("d"), []byte("c"), []byte("b"), []byte("a")) 496 require.NoError(t, err) 497 498 return nil 499 }) 500 require.NoError(t, err) 501 502 listIdx := db.Index.list.getWithDefault(1) 503 item, ok := listIdx.Items[string(key)].PopMin() 504 r := item.record 505 require.True(t, ok) 506 require.NotNil(t, r.Value) 507 require.Equal(t, []byte("a"), r.Value) 508 }) 509 } 510 511 func TestTx_ListEntryIdxMode_HintKeyAndRAMIdxMode(t *testing.T) { 512 bucket := "bucket" 513 key := GetTestBytes(0) 514 515 opts := &DefaultOptions 516 opts.EntryIdxMode = HintKeyAndRAMIdxMode 517 518 // HintKeyAndRAMIdxMode 519 runNutsDBTest(t, opts, func(t *testing.T, db *DB) { 520 txCreateBucket(t, db, DataStructureList, bucket, nil) 521 err := db.Update(func(tx *Tx) error { 522 err := tx.LPush(bucket, key, []byte("d"), []byte("c"), []byte("b"), []byte("a")) 523 require.NoError(t, err) 524 525 return nil 526 }) 527 require.NoError(t, err) 528 529 listIdx := db.Index.list.getWithDefault(1) 530 item, ok := listIdx.Items[string(key)].PopMin() 531 r := item.record 532 require.True(t, ok) 533 require.Nil(t, r.Value) 534 535 val, err := db.getValueByRecord(r) 536 require.NoError(t, err) 537 require.Equal(t, []byte("a"), val) 538 }) 539 }